diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java index 6829f0838..35cec8c38 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/java/com/alibaba/cloud/examples/Application.java @@ -21,7 +21,7 @@ import java.io.StringReader; import java.util.Properties; import java.util.concurrent.Executor; -import com.alibaba.cloud.nacos.NacosConfigProperties; +import com.alibaba.cloud.nacos.NacosConfigManager; import com.alibaba.nacos.api.config.listener.Listener; import org.springframework.beans.factory.annotation.Autowired; @@ -57,14 +57,14 @@ class SampleRunner implements ApplicationRunner { int userAge; @Autowired - private NacosConfigProperties nacosConfigProperties; + private NacosConfigManager nacosConfigManager; @Override public void run(ApplicationArguments args) throws Exception { System.out.println( String.format("Initial username=%s, userAge=%d", userName, userAge)); - nacosConfigProperties.configServiceInstance().addListener( + nacosConfigManager.getConfigService().addListener( "nacos-config-example.properties", "DEFAULT_GROUP", new Listener() { /** @@ -108,12 +108,12 @@ class SampleController { Integer age; @Autowired - private NacosConfigProperties nacosConfigProperties; + private NacosConfigManager nacosConfigManager; @RequestMapping("/user") public String simple() { return "Hello Nacos Config!" + "Hello " + userName + " " + age + "!" - + nacosConfigProperties.configServiceInstance(); + + nacosConfigManager.getConfigService(); } } diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties index d6906d1c9..2fc0eab44 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/src/main/resources/bootstrap.properties @@ -1,4 +1,19 @@ spring.application.name=nacos-config-example spring.cloud.nacos.config.server-addr=127.0.0.1:8848 -spring.cloud.nacos.config.shared-data-ids=base-common.properties,common.properties -spring.cloud.nacos.config.refreshable-dataids=common.properties + +#spring.cloud.nacos.config.refreshable-dataids=common.properties +#spring.cloud.nacos.config.shared-data-ids=common.properties,base-common.properties +spring.cloud.nacos.config.shared-configs[0]= common333.properties +spring.cloud.nacos.config.shared-configs[1].data-id= common111.properties +spring.cloud.nacos.config.shared-configs[1].group= GROUP_APP1 +spring.cloud.nacos.config.shared-configs[1].refresh= true +spring.cloud.nacos.config.shared-configs[2]= common222.properties + +#spring.cloud.nacos.config.ext-config[0]=ext.properties +spring.cloud.nacos.config.extension-configs[0].data-id= extension1.properties +spring.cloud.nacos.config.extension-configs[0].refresh= true +spring.cloud.nacos.config.extension-configs[1]= extension2.properties + + +#spring.cloud.nacos.config.refresh-enabled=true + diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java index f754d19d2..05acc40f3 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigAutoConfiguration.java @@ -54,13 +54,24 @@ public class NacosConfigAutoConfiguration { return new NacosRefreshHistory(); } + @Bean + public NacosConfigManager nacosConfigManager( + NacosConfigProperties nacosConfigProperties) { + return new NacosConfigManager(nacosConfigProperties); + } + @Bean public NacosContextRefresher nacosContextRefresher( - NacosConfigProperties configProperties, + NacosConfigManager nacosConfigManager, NacosRefreshProperties nacosRefreshProperties, - NacosRefreshHistory refreshHistory) { - return new NacosContextRefresher(nacosRefreshProperties, refreshHistory, - configProperties.configServiceInstance()); + NacosRefreshHistory nacosRefreshHistory) { + // Compatible with older configurations + if (nacosConfigManager.getNacosConfigProperties().isRefreshEnabled() + && null != nacosRefreshProperties + && !nacosRefreshProperties.isEnabled()) { + nacosConfigManager.getNacosConfigProperties().setRefreshEnabled(false); + } + return new NacosContextRefresher(nacosConfigManager, nacosRefreshHistory); } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.java index 70aeaf83a..4e670c603 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigBootstrapConfiguration.java @@ -37,9 +37,16 @@ public class NacosConfigBootstrapConfiguration { } @Bean - public NacosPropertySourceLocator nacosPropertySourceLocator( + @ConditionalOnMissingBean + public NacosConfigManager nacosConfigManager( NacosConfigProperties nacosConfigProperties) { - return new NacosPropertySourceLocator(nacosConfigProperties); + return new NacosConfigManager(nacosConfigProperties); + } + + @Bean + public NacosPropertySourceLocator nacosPropertySourceLocator( + NacosConfigManager nacosConfigManager) { + return new NacosPropertySourceLocator(nacosConfigManager); } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java new file mode 100644 index 000000000..510dabf4f --- /dev/null +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigManager.java @@ -0,0 +1,78 @@ +/* + * Copyright 2013-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.cloud.nacos; + +import java.util.Objects; + +import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.exception.NacosException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author zkzlx + */ +public class NacosConfigManager { + + private static final Logger log = LoggerFactory.getLogger(NacosConfigManager.class); + + private static ConfigService service = null; + + private NacosConfigProperties nacosConfigProperties; + + public NacosConfigManager(NacosConfigProperties nacosConfigProperties) { + this.nacosConfigProperties = nacosConfigProperties; + // Compatible with older code in NacosConfigProperties,It will be deleted in the + // future. + createConfigService(nacosConfigProperties); + } + + /** + * Compatible with old design,It will be perfected in the future. + */ + static ConfigService createConfigService( + NacosConfigProperties nacosConfigProperties) { + if (Objects.isNull(service)) { + synchronized (NacosConfigManager.class) { + try { + service = NacosFactory.createConfigService( + nacosConfigProperties.assembleConfigServiceProperties()); + } + catch (NacosException e) { + log.error(e.getMessage()); + throw new NacosConnectionFailureException( + nacosConfigProperties.getServerAddr(), e.getMessage(), e); + } + } + } + return service; + } + + public ConfigService getConfigService() { + if (Objects.isNull(service)) { + createConfigService(this.nacosConfigProperties); + } + return service; + } + + public NacosConfigProperties getNacosConfigProperties() { + return nacosConfigProperties; + } + +} diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java index 0a5005d3a..db865160f 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java @@ -16,25 +16,28 @@ package com.alibaba.cloud.nacos; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.PostConstruct; -import com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureException; -import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.spring.util.PropertySourcesUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; @@ -69,6 +72,16 @@ public class NacosConfigProperties { */ public static final String PREFIX = "spring.cloud.nacos.config"; + /** + * COMMAS , . + */ + public static final String COMMAS = ","; + + /** + * SEPARATOR , . + */ + public static final String SEPARATOR = "[,]"; + private static final Pattern PATTERN = Pattern.compile("-(\\w)"); private static final Logger log = LoggerFactory @@ -185,22 +198,21 @@ public class NacosConfigProperties { private String name; /** - * the dataids for configurable multiple shared configurations , multiple separated by - * commas . + * a set of shared configurations .e.g: + * spring.cloud.nacos.config.shared-configs[0]=xxx . */ - private String sharedDataids; + private List sharedConfigs; /** - * refreshable dataids , multiple separated by commas . + * a set of extensional configurations .e.g: + * spring.cloud.nacos.config.extension-configs[0]=xxx . */ - private String refreshableDataids; + private List extensionConfigs; /** - * a set of extended configurations . + * the master switch for refresh configuration, it default opened(true). */ - private List extConfig; - - private static ConfigService configService; + private boolean refreshEnabled = true; // todo sts support @@ -336,61 +348,163 @@ public class NacosConfigProperties { return name; } - public String getSharedDataids() { - return sharedDataids; + public void setName(String name) { + this.name = name; } - public void setSharedDataids(String sharedDataids) { - this.sharedDataids = sharedDataids; + public Environment getEnvironment() { + return environment; } - public String getRefreshableDataids() { - return refreshableDataids; + public void setEnvironment(Environment environment) { + this.environment = environment; } - public void setRefreshableDataids(String refreshableDataids) { - this.refreshableDataids = refreshableDataids; + public List getSharedConfigs() { + return sharedConfigs; } - public List getExtConfig() { - return extConfig; + public void setSharedConfigs(List sharedConfigs) { + this.sharedConfigs = sharedConfigs; } - public void setExtConfig(List extConfig) { - this.extConfig = extConfig; + public List getExtensionConfigs() { + return extensionConfigs; } - public void setName(String name) { - this.name = name; + public void setExtensionConfigs(List extensionConfigs) { + this.extensionConfigs = extensionConfigs; } - public Environment getEnvironment() { - return environment; + public boolean isRefreshEnabled() { + return refreshEnabled; } - public void setEnvironment(Environment environment) { - this.environment = environment; + public void setRefreshEnabled(boolean refreshEnabled) { + this.refreshEnabled = refreshEnabled; + } + + /** + * recommend to use {@link NacosConfigProperties#sharedConfigs} . + * @return string + */ + @Deprecated + @DeprecatedConfigurationProperty( + reason = "replaced to NacosConfigProperties#sharedConfigs and not use it at the same time.", + replacement = PREFIX + ".shared-configs[x]") + public String getSharedDataids() { + return null == getSharedConfigs() ? null : getSharedConfigs().stream() + .map(Config::getDataId).collect(Collectors.joining(COMMAS)); + } + + /** + * recommend to use {@link NacosConfigProperties#sharedConfigs} and not use it at the + * same time . + * @param sharedDataids the dataids for configurable multiple shared configurations , + * multiple separated by commas . + */ + @Deprecated + public void setSharedDataids(String sharedDataids) { + if (null != sharedDataids && sharedDataids.trim().length() > 0) { + List list = new ArrayList<>(); + Stream.of(sharedDataids.split(SEPARATOR)) + .forEach(dataId -> list.add(new Config(dataId.trim()))); + this.compatibleSharedConfigs(list); + } + } + + /** + * Not providing support,the need to refresh is specified by the respective refresh + * configuration and not use it at the same time . + * @return string + */ + @Deprecated + @DeprecatedConfigurationProperty( + reason = "replaced to NacosConfigProperties#sharedConfigs and not use it at the same time.", + replacement = PREFIX + ".shared-configs[x].refresh") + public String getRefreshableDataids() { + return null == getSharedConfigs() ? null + : getSharedConfigs().stream().filter(Config::isRefresh) + .map(Config::getDataId).collect(Collectors.joining(COMMAS)); + } + + /** + * Not providing support,the need to refresh is specified by the respective refresh + * configuration and not use it at the same time . + * @param refreshableDataids refreshable dataids ,multiple separated by commas . + */ + @Deprecated + public void setRefreshableDataids(String refreshableDataids) { + if (null != refreshableDataids && refreshableDataids.trim().length() > 0) { + List list = new ArrayList<>(); + Stream.of(refreshableDataids.split(SEPARATOR)).forEach( + dataId -> list.add(new Config(dataId.trim()).setRefresh(true))); + this.compatibleSharedConfigs(list); + } + } + + private void compatibleSharedConfigs(List configList) { + if (null != this.getSharedConfigs()) { + configList.addAll(this.getSharedConfigs()); + } + List result = new ArrayList<>(); + configList.stream() + .collect(Collectors.groupingBy(cfg -> (cfg.getGroup() + cfg.getDataId()), + () -> new ConcurrentHashMap<>(new LinkedHashMap<>()), + Collectors.toList())) + .forEach((key, list) -> { + list.stream() + .reduce((a, b) -> new Config(a.getDataId(), a.getGroup(), + a.isRefresh() || (b != null && b.isRefresh()))) + .ifPresent(result::add); + }); + this.setSharedConfigs(result); + } + + /** + * recommend to use + * {@link com.alibaba.cloud.nacos.NacosConfigProperties#extensionConfigs} and not use + * it at the same time . + * @return extensionConfigs + */ + @Deprecated + @DeprecatedConfigurationProperty( + reason = "replaced to NacosConfigProperties#extensionConfigs and not use it at the same time .", + replacement = PREFIX + ".extension-configs[x]") + public List getExtConfig() { + return this.getExtensionConfigs(); + } + + @Deprecated + public void setExtConfig(List extConfig) { + this.setExtensionConfigs(extConfig); } /** + * recommend to use {@link NacosConfigManager#getConfigService()}. * @return ConfigService */ @Deprecated public ConfigService configServiceInstance() { - if (null == configService) { - try { - configService = NacosFactory - .createConfigService(getConfigServiceProperties()); - } - catch (NacosException e) { - throw new NacosConnectionFailureException(this.getServerAddr(), - e.getMessage(), e); - } - } - return configService; + // The following code will be migrated + return NacosConfigManager.createConfigService(this); } + /** + * recommend to use {@link NacosConfigProperties#assembleConfigServiceProperties()}. + * @return ConfigServiceProperties + */ + @Deprecated public Properties getConfigServiceProperties() { + return this.assembleConfigServiceProperties(); + } + + /** + * assemble properties for configService. (cause by rename : Remove the interference + * of auto prompts when writing,because autocue is based on get method. + * @return properties + */ + public Properties assembleConfigServiceProperties() { Properties properties = new Properties(); properties.put(SERVER_ADDR, Objects.toString(this.serverAddr, "")); properties.put(ENCODE, Objects.toString(this.encode, "")); @@ -441,12 +555,16 @@ public class NacosConfigProperties { return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\'' + ", encode='" + encode + '\'' + ", group='" + group + '\'' + ", prefix='" + prefix + '\'' + ", fileExtension='" + fileExtension + '\'' - + ", timeout=" + timeout + ", endpoint='" + endpoint + '\'' - + ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\'' - + ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath - + '\'' + ", clusterName='" + clusterName + '\'' + ", name='" + name + '\'' - + ", sharedDataids='" + sharedDataids + '\'' + ", refreshableDataids='" - + refreshableDataids + '\'' + ", extConfig=" + extConfig + '}'; + + ", timeout=" + timeout + ", maxRetry='" + maxRetry + '\'' + + ", configLongPollTimeout='" + configLongPollTimeout + '\'' + + ", configRetryTime='" + configRetryTime + '\'' + + ", enableRemoteSyncConfig=" + enableRemoteSyncConfig + ", endpoint='" + + endpoint + '\'' + ", namespace='" + namespace + '\'' + ", accessKey='" + + accessKey + '\'' + ", secretKey='" + secretKey + '\'' + + ", contextPath='" + contextPath + '\'' + ", clusterName='" + clusterName + + '\'' + ", name='" + name + '\'' + '\'' + ", shares=" + sharedConfigs + + ", extensions=" + extensionConfigs + ", refreshEnabled=" + + refreshEnabled + '}'; } public static class Config { @@ -466,28 +584,77 @@ public class NacosConfigProperties { */ private boolean refresh = false; + public Config() { + } + + public Config(String dataId) { + this.dataId = dataId; + } + + public Config(String dataId, String group) { + this(dataId); + this.group = group; + } + + public Config(String dataId, boolean refresh) { + this(dataId); + this.refresh = refresh; + } + + public Config(String dataId, String group, boolean refresh) { + this(dataId, group); + this.refresh = refresh; + } + public String getDataId() { return dataId; } - public void setDataId(String dataId) { + public Config setDataId(String dataId) { this.dataId = dataId; + return this; } public String getGroup() { return group; } - public void setGroup(String group) { + public Config setGroup(String group) { this.group = group; + return this; } public boolean isRefresh() { return refresh; } - public void setRefresh(boolean refresh) { + public Config setRefresh(boolean refresh) { this.refresh = refresh; + return this; + } + + @Override + public String toString() { + return "Config{" + "dataId='" + dataId + '\'' + ", group='" + group + '\'' + + ", refresh=" + refresh + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Config config = (Config) o; + return refresh == config.refresh && Objects.equals(dataId, config.dataId) + && Objects.equals(group, config.group); + } + + @Override + public int hashCode() { + return Objects.hash(dataId, group, refresh); } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosPropertySourceRepository.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosPropertySourceRepository.java index 470646215..718515ff8 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosPropertySourceRepository.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosPropertySourceRepository.java @@ -41,14 +41,42 @@ public final class NacosPropertySourceRepository { return new ArrayList<>(NACOS_PROPERTY_SOURCE_REPOSITORY.values()); } + /** + * recommend to use {@link NacosPropertySourceRepository#collectNacosPropertySource}. + * @param nacosPropertySource nacosPropertySource + */ + @Deprecated public static void collectNacosPropertySources( NacosPropertySource nacosPropertySource) { NACOS_PROPERTY_SOURCE_REPOSITORY.putIfAbsent(nacosPropertySource.getDataId(), nacosPropertySource); } + /** + * recommend to use + * {@link NacosPropertySourceRepository#getNacosPropertySource(java.lang.String, java.lang.String)}. + * @param dataId dataId + * @return NacosPropertySource + */ + @Deprecated public static NacosPropertySource getNacosPropertySource(String dataId) { return NACOS_PROPERTY_SOURCE_REPOSITORY.get(dataId); } + public static void collectNacosPropertySource( + NacosPropertySource nacosPropertySource) { + NACOS_PROPERTY_SOURCE_REPOSITORY + .putIfAbsent(getMapKey(nacosPropertySource.getDataId(), + nacosPropertySource.getGroup()), nacosPropertySource); + } + + public static NacosPropertySource getNacosPropertySource(String dataId, + String group) { + return NACOS_PROPERTY_SOURCE_REPOSITORY.get(getMapKey(dataId, group)); + } + + public static String getMapKey(String dataId, String group) { + return String.format("%s$%s", String.valueOf(dataId), String.valueOf(group)); + } + } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java index 82c93f5ba..997fd378e 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySource.java @@ -49,7 +49,7 @@ public class NacosPropertySource extends MapPropertySource { NacosPropertySource(String group, String dataId, Map source, Date timestamp, boolean isRefreshable) { - super(dataId, source); + super(String.format("%s#%s", dataId, group), source); this.group = group; this.dataId = dataId; this.timestamp = timestamp; diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceBuilder.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceBuilder.java index acd2a49d4..3f650b5e8 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceBuilder.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceBuilder.java @@ -76,7 +76,7 @@ public class NacosPropertySourceBuilder { Properties p = loadNacosData(dataId, group, fileExtension); NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId, propertiesToMap(p), new Date(), isRefreshable); - NacosPropertySourceRepository.collectNacosPropertySources(nacosPropertySource); + NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource); return nacosPropertySource; } @@ -90,10 +90,11 @@ public class NacosPropertySourceBuilder { dataId, group); return EMPTY_PROPERTIES; } - log.info(String.format( - "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId, - group, data)); - + if (log.isDebugEnabled()) { + log.debug(String.format( + "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId, + group, data)); + } Properties properties = NacosDataParserHandler.getInstance() .parseNacosData(data, fileExtension); return properties == null ? EMPTY_PROPERTIES : properties; diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java index af51b3aab..ef075bcb6 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/client/NacosPropertySourceLocator.java @@ -18,6 +18,7 @@ package com.alibaba.cloud.nacos.client; import java.util.List; +import com.alibaba.cloud.nacos.NacosConfigManager; import com.alibaba.cloud.nacos.NacosConfigProperties; import com.alibaba.cloud.nacos.NacosPropertySourceRepository; import com.alibaba.cloud.nacos.parser.NacosDataParserHandler; @@ -50,20 +51,31 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { private static final String DOT = "."; - private static final String SHARED_CONFIG_SEPARATOR_CHAR = "[,]"; - private NacosPropertySourceBuilder nacosPropertySourceBuilder; private NacosConfigProperties nacosConfigProperties; + private NacosConfigManager nacosConfigManager; + + /** + * recommend to use + * {@link NacosPropertySourceLocator#NacosPropertySourceLocator(com.alibaba.cloud.nacos.NacosConfigManager)}. + * @param nacosConfigProperties nacosConfigProperties + */ + @Deprecated public NacosPropertySourceLocator(NacosConfigProperties nacosConfigProperties) { this.nacosConfigProperties = nacosConfigProperties; } + public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) { + this.nacosConfigManager = nacosConfigManager; + this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); + } + @Override public PropertySource locate(Environment env) { nacosConfigProperties.setEnvironment(env); - ConfigService configService = nacosConfigProperties.configServiceInstance(); + ConfigService configService = nacosConfigManager.getConfigService(); if (null == configService) { log.warn("no instance of config service found, can't load config from nacos"); @@ -93,68 +105,39 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { return composite; } + /** + * load shared configuration. + */ private void loadSharedConfiguration( CompositePropertySource compositePropertySource) { - String sharedDataIds = nacosConfigProperties.getSharedDataids(); - String refreshDataIds = nacosConfigProperties.getRefreshableDataids(); - - if (sharedDataIds == null || sharedDataIds.trim().length() == 0) { - return; - } - - String[] sharedDataIdArray = sharedDataIds.split(SHARED_CONFIG_SEPARATOR_CHAR); - checkDataIdFileExtension(sharedDataIdArray); - - for (int i = 0; i < sharedDataIdArray.length; i++) { - String dataId = sharedDataIdArray[i]; - String fileExtension = dataId.substring(dataId.lastIndexOf(".") + 1); - boolean isRefreshable = checkDataIdIsRefreshable(refreshDataIds, - sharedDataIdArray[i]); - - loadNacosDataIfPresent(compositePropertySource, dataId, "DEFAULT_GROUP", - fileExtension, isRefreshable); + List sharedConfigs = nacosConfigProperties + .getSharedConfigs(); + if (!CollectionUtils.isEmpty(sharedConfigs)) { + checkConfiguration(sharedConfigs, "shared-configs"); + loadNacosConfiguration(compositePropertySource, sharedConfigs); } } + /** + * load extensional configuration. + */ private void loadExtConfiguration(CompositePropertySource compositePropertySource) { List extConfigs = nacosConfigProperties - .getExtConfig(); - - if (CollectionUtils.isEmpty(extConfigs)) { - return; - } - - checkExtConfiguration(extConfigs); - - for (NacosConfigProperties.Config config : extConfigs) { - String dataId = config.getDataId(); - String fileExtension = dataId.substring(dataId.lastIndexOf(DOT) + 1); - loadNacosDataIfPresent(compositePropertySource, dataId, config.getGroup(), - fileExtension, config.isRefresh()); + .getExtensionConfigs(); + if (!CollectionUtils.isEmpty(extConfigs)) { + checkConfiguration(extConfigs, "extension-configs"); + loadNacosConfiguration(compositePropertySource, extConfigs); } } - private void checkExtConfiguration(List extConfigs) { - String[] dataIds = new String[extConfigs.size()]; - for (int i = 0; i < extConfigs.size(); i++) { - String dataId = extConfigs.get(i).getDataId(); - if (dataId == null || dataId.trim().length() == 0) { - throw new IllegalStateException(String.format( - "the [ spring.cloud.nacos.config.ext-config[%s] ] must give a dataId", - i)); - } - dataIds[i] = dataId; - } - checkDataIdFileExtension(dataIds); - } - + /** + * load configuration of application. + */ private void loadApplicationConfiguration( CompositePropertySource compositePropertySource, String dataIdPrefix, NacosConfigProperties properties, Environment environment) { - String fileExtension = properties.getFileExtension(); String nacosGroup = properties.getGroup(); - // load directly once by default loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, fileExtension, true); @@ -167,6 +150,33 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true); } + + } + + private void loadNacosConfiguration(final CompositePropertySource composite, + List configs) { + for (NacosConfigProperties.Config config : configs) { + String dataId = config.getDataId(); + String fileExtension = dataId.substring(dataId.lastIndexOf(DOT) + 1); + loadNacosDataIfPresent(composite, dataId, config.getGroup(), fileExtension, + config.isRefresh()); + } + } + + private void checkConfiguration(List configs, + String tips) { + String[] dataIds = new String[configs.size()]; + for (int i = 0; i < configs.size(); i++) { + String dataId = configs.get(i).getDataId(); + if (dataId == null || dataId.trim().length() == 0) { + throw new IllegalStateException(String.format( + "the [ spring.cloud.nacos.config.%s[%s] ] must give a dataId", + tips, i)); + } + dataIds[i] = dataId; + } + // Just decide that the current dataId must have a suffix + NacosDataParserHandler.getInstance().checkDataId(dataIds); } private void loadNacosDataIfPresent(final CompositePropertySource composite, @@ -187,7 +197,8 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { final String group, String fileExtension, boolean isRefreshable) { if (NacosContextRefresher.getRefreshCount() != 0) { if (!isRefreshable) { - return NacosPropertySourceRepository.getNacosPropertySource(dataId); + return NacosPropertySourceRepository.getNacosPropertySource(dataId, + group); } } return nacosPropertySourceBuilder.build(dataId, group, fileExtension, @@ -209,27 +220,8 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { composite.addFirstPropertySource(nacosPropertySource); } - private static void checkDataIdFileExtension(String[] dataIdArray) { - if (dataIdArray == null || dataIdArray.length < 1) { - throw new IllegalStateException("The dataId cannot be empty"); - } - // Just decide that the current dataId must have a suffix - NacosDataParserHandler.getInstance().checkDataId(dataIdArray); - } - - private boolean checkDataIdIsRefreshable(String refreshDataIds, String sharedDataId) { - if (StringUtils.isEmpty(refreshDataIds)) { - return false; - } - - String[] refreshDataIdArray = refreshDataIds.split(SHARED_CONFIG_SEPARATOR_CHAR); - for (String refreshDataId : refreshDataIdArray) { - if (refreshDataId.equals(sharedDataId)) { - return true; - } - } - - return false; + public void setNacosConfigManager(NacosConfigManager nacosConfigManager) { + this.nacosConfigManager = nacosConfigManager; } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java index 762ba0190..34decf74a 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java @@ -16,7 +16,7 @@ package com.alibaba.cloud.nacos.endpoint; -import com.alibaba.cloud.nacos.NacosConfigProperties; +import com.alibaba.cloud.nacos.NacosConfigManager; import com.alibaba.cloud.nacos.refresh.NacosRefreshHistory; import org.springframework.beans.factory.annotation.Autowired; @@ -37,7 +37,7 @@ import org.springframework.context.annotation.Bean; public class NacosConfigEndpointAutoConfiguration { @Autowired - private NacosConfigProperties nacosConfigProperties; + private NacosConfigManager nacosConfigManager; @Autowired private NacosRefreshHistory nacosRefreshHistory; @@ -46,13 +46,13 @@ public class NacosConfigEndpointAutoConfiguration { @ConditionalOnEnabledEndpoint @Bean public NacosConfigEndpoint nacosConfigEndpoint() { - return new NacosConfigEndpoint(nacosConfigProperties, nacosRefreshHistory); + return new NacosConfigEndpoint(nacosConfigManager.getNacosConfigProperties(), + nacosRefreshHistory); } @Bean public NacosConfigHealthIndicator nacosConfigHealthIndicator() { - return new NacosConfigHealthIndicator( - nacosConfigProperties.configServiceInstance()); + return new NacosConfigHealthIndicator(nacosConfigManager.getConfigService()); } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosContextRefresher.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosContextRefresher.java index d6a0458ad..609a6c617 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosContextRefresher.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosContextRefresher.java @@ -16,19 +16,17 @@ package com.alibaba.cloud.nacos.refresh; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import com.alibaba.cloud.nacos.NacosConfigManager; +import com.alibaba.cloud.nacos.NacosConfigProperties; import com.alibaba.cloud.nacos.NacosPropertySourceRepository; import com.alibaba.cloud.nacos.client.NacosPropertySource; import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import org.slf4j.Logger; @@ -39,7 +37,6 @@ import org.springframework.cloud.endpoint.event.RefreshEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; -import org.springframework.util.StringUtils; /** * On application start up, NacosContextRefresher add nacos listeners to all application @@ -57,9 +54,11 @@ public class NacosContextRefresher private static final AtomicLong REFRESH_COUNT = new AtomicLong(0); - private final NacosRefreshProperties refreshProperties; + private NacosConfigProperties nacosConfigProperties; - private final NacosRefreshHistory refreshHistory; + private final boolean isRefreshEnabled; + + private final NacosRefreshHistory nacosRefreshHistory; private final ConfigService configService; @@ -69,10 +68,26 @@ public class NacosContextRefresher private Map listenerMap = new ConcurrentHashMap<>(16); + public NacosContextRefresher(NacosConfigManager nacosConfigManager, + NacosRefreshHistory refreshHistory) { + this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties(); + this.nacosRefreshHistory = refreshHistory; + this.configService = nacosConfigManager.getConfigService(); + this.isRefreshEnabled = this.nacosConfigProperties.isRefreshEnabled(); + } + + /** + * recommend to use + * {@link NacosContextRefresher#NacosContextRefresher(NacosConfigManager, NacosRefreshHistory)}. + * @param refreshProperties refreshProperties + * @param refreshHistory refreshHistory + * @param configService configService + */ + @Deprecated public NacosContextRefresher(NacosRefreshProperties refreshProperties, NacosRefreshHistory refreshHistory, ConfigService configService) { - this.refreshProperties = refreshProperties; - this.refreshHistory = refreshHistory; + this.isRefreshEnabled = refreshProperties.isEnabled(); + this.nacosRefreshHistory = refreshHistory; this.configService = configService; } @@ -89,58 +104,70 @@ public class NacosContextRefresher this.applicationContext = applicationContext; } + /** + * register Nacos Listeners. + */ private void registerNacosListenersForApplications() { - if (refreshProperties.isEnabled()) { - for (NacosPropertySource nacosPropertySource : NacosPropertySourceRepository + if (isRefreshEnabled()) { + for (NacosPropertySource propertySource : NacosPropertySourceRepository .getAll()) { - - if (!nacosPropertySource.isRefreshable()) { + if (!propertySource.isRefreshable()) { continue; } - - String dataId = nacosPropertySource.getDataId(); - registerNacosListener(nacosPropertySource.getGroup(), dataId); + String dataId = propertySource.getDataId(); + registerNacosListener(propertySource.getGroup(), dataId); } } } - private void registerNacosListener(final String group, final String dataId) { - - Listener listener = listenerMap.computeIfAbsent(dataId, i -> new Listener() { - @Override - public void receiveConfigInfo(String configInfo) { - refreshCountIncrement(); - String md5 = ""; - if (!StringUtils.isEmpty(configInfo)) { - try { - MessageDigest md = MessageDigest.getInstance("MD5"); - md5 = new BigInteger(1, md.digest(configInfo.getBytes("UTF-8"))) - .toString(16); + private void registerNacosListener(final String groupKey, final String dataKey) { + String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey); + Listener listener = listenerMap.computeIfAbsent(key, + lst -> new AbstractSharedListener() { + @Override + public void innerReceive(String dataId, String group, + String configInfo) { + refreshCountIncrement(); + nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo); + // todo feature: support single refresh for listening + applicationContext.publishEvent( + new RefreshEvent(this, null, "Refresh Nacos config")); + if (log.isDebugEnabled()) { + log.debug(String.format( + "Refresh Nacos config group=%s,dataId=%s,configInfo=%s", + group, dataId, configInfo)); + } } - catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - log.warn("[Nacos] unable to get md5 for dataId: " + dataId, e); - } - } - refreshHistory.add(dataId, md5); - applicationContext.publishEvent( - new RefreshEvent(this, null, "Refresh Nacos config")); - if (log.isDebugEnabled()) { - log.debug("Refresh Nacos config group " + group + ",dataId" + dataId); - } - } - - @Override - public Executor getExecutor() { - return null; - } - }); - + }); try { - configService.addListener(dataId, group, listener); + configService.addListener(dataKey, groupKey, listener); } catch (NacosException e) { - e.printStackTrace(); + log.warn(String.format( + "register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey, + groupKey), e); + } + } + + public NacosConfigProperties getNacosConfigProperties() { + return nacosConfigProperties; + } + + public NacosContextRefresher setNacosConfigProperties( + NacosConfigProperties nacosConfigProperties) { + this.nacosConfigProperties = nacosConfigProperties; + return this; + } + + public boolean isRefreshEnabled() { + if (null == nacosConfigProperties) { + return isRefreshEnabled; + } + // Compatible with older configurations + if (nacosConfigProperties.isRefreshEnabled() && !isRefreshEnabled) { + return false; } + return isRefreshEnabled; } public static long getRefreshCount() { diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshHistory.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshHistory.java index 1297061c9..bf8cc0d6a 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshHistory.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshHistory.java @@ -16,26 +16,55 @@ package com.alibaba.cloud.nacos.refresh; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedList; +import java.util.Map; + +import org.springframework.util.StringUtils; public class NacosRefreshHistory { private static final int MAX_SIZE = 20; - private LinkedList records = new LinkedList<>(); + private final LinkedList records = new LinkedList<>(); + + private final ThreadLocal DATE_FORMAT = ThreadLocal + .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); - private ThreadLocal dateFormat = new ThreadLocal() { - @Override - protected DateFormat initialValue() { - return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private MessageDigest md; + + public NacosRefreshHistory() { + try { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException ignored) { } - }; + } + /** + * recommend to use + * {@link NacosRefreshHistory#addRefreshRecord(java.lang.String, java.lang.String, java.lang.String)}. + * @param dataId dataId + * @param md5 md5 + */ + @Deprecated public void add(String dataId, String md5) { - records.addFirst(new Record(dateFormat.get().format(new Date()), dataId, md5)); + records.addFirst( + new Record(DATE_FORMAT.get().format(new Date()), dataId, "", md5, null)); + if (records.size() > MAX_SIZE) { + records.removeLast(); + } + } + + public void addRefreshRecord(String dataId, String group, String data) { + records.addFirst(new Record(DATE_FORMAT.get().format(new Date()), dataId, group, + md5(data), null)); if (records.size() > MAX_SIZE) { records.removeLast(); } @@ -45,32 +74,56 @@ public class NacosRefreshHistory { return records; } -} + private String md5(String data) { + if (StringUtils.isEmpty(data)) { + return null; + } + if (null == md) { + try { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException ignored) { + return "unable to get md5"; + } + } + return new BigInteger(1, md.digest(data.getBytes(StandardCharsets.UTF_8))) + .toString(16); + } -class Record { + static class Record { - private final String timestamp; + private final String timestamp; - private final String dataId; + private final String dataId; - private final String md5; + private final String group; - Record(String timestamp, String dataId, String md5) { - this.timestamp = timestamp; - this.dataId = dataId; - this.md5 = md5; - } + private final String md5; - public String getTimestamp() { - return timestamp; - } + Record(String timestamp, String dataId, String group, String md5, + Map last) { + this.timestamp = timestamp; + this.dataId = dataId; + this.group = group; + this.md5 = md5; + } - public String getDataId() { - return dataId; - } + public String getTimestamp() { + return timestamp; + } + + public String getDataId() { + return dataId; + } + + public String getGroup() { + return group; + } + + public String getMd5() { + return md5; + } - public String getMd5() { - return md5; } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshProperties.java index 9874eebd7..0dae5d169 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/refresh/NacosRefreshProperties.java @@ -16,15 +16,22 @@ package com.alibaba.cloud.nacos.refresh; +import com.alibaba.cloud.nacos.NacosConfigProperties; + import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @author xiaojing */ +@Deprecated @Component public class NacosRefreshProperties { + /** + * recommend to use {@code NacosConfigProperties#refreshEnabled} + * {@link NacosConfigProperties#setRefreshEnabled(boolean)}. + */ @Value("${spring.cloud.nacos.config.refresh.enabled:true}") private boolean enabled = true; diff --git a/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 3d322070e..f5c0ba190 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -3,7 +3,6 @@ { "name": "spring.cloud.nacos.server-addr", "type": "java.lang.String", - "defaultValue": "localhost:8848", "description": "nacos server address." }, { @@ -35,16 +34,33 @@ "type": "java.lang.String", "description": "the dataids for configurable multiple shared configurations , multiple separated by commas ." }, + { + "name": "spring.cloud.nacos.config.shared-configs", + "type": "java.util.List", + "description": "a set of shared configurations .e.g: spring.cloud.nacos.config.shared-configs[0]=xxx ." + }, + { "name": "spring.cloud.nacos.config.refreshable-dataids", "type": "java.lang.String", - "description": "refreshable dataids , multiple separated by commas ." + "description": "refreshable dataids , multiple separated by commas .Not providing support,the need to refresh is specified by the respective refresh configuration." }, { "name": "spring.cloud.nacos.config.ext-config", "type": "java.util.List", "description": "a set of extended configurations ." }, + { + "name": "spring.cloud.nacos.config.extension-configs", + "type": "java.util.List", + "description": "a set of extensional configurations .e.g: spring.cloud.nacos.config.extension-configs[0]=xxx ." + }, + { + "name": "spring.cloud.nacos.config.refresh-enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "the master switch for refresh configuration, it default opened(true)." + }, { "name": "spring.cloud.nacos.config.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-alibaba-nacos-config/src/test/java/com/alibaba/cloud/nacos/NacosConfigurationNewTest.java b/spring-cloud-alibaba-nacos-config/src/test/java/com/alibaba/cloud/nacos/NacosConfigurationNewTest.java new file mode 100644 index 000000000..7ec80f719 --- /dev/null +++ b/spring-cloud-alibaba-nacos-config/src/test/java/com/alibaba/cloud/nacos/NacosConfigurationNewTest.java @@ -0,0 +1,242 @@ +/* + * Copyright 2013-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.cloud.nacos; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Map; + +import com.alibaba.cloud.nacos.NacosConfigProperties.Config; +import com.alibaba.cloud.nacos.client.NacosPropertySourceLocator; +import com.alibaba.cloud.nacos.endpoint.NacosConfigEndpoint; +import com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration; +import com.alibaba.cloud.nacos.refresh.NacosRefreshHistory; +import com.alibaba.nacos.client.config.NacosConfigService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.api.support.MethodProxy; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE; + +/** + * @author xiaojing + */ +@RunWith(PowerMockRunner.class) +@PowerMockIgnore("javax.management.*") +@PowerMockRunnerDelegate(SpringRunner.class) +@PrepareForTest({ NacosConfigService.class }) +@SpringBootTest(classes = NacosConfigurationNewTest.TestConfig.class, properties = { + "spring.application.name=myTestService1", "spring.profiles.active=dev,test", + "spring.cloud.nacos.config.server-addr=127.0.0.1:8848", + "spring.cloud.nacos.config.namespace=test-namespace", + "spring.cloud.nacos.config.encode=utf-8", + "spring.cloud.nacos.config.timeout=1000", + "spring.cloud.nacos.config.group=test-group", + "spring.cloud.nacos.config.name=test-name", + "spring.cloud.nacos.config.cluster-name=test-cluster", + "spring.cloud.nacos.config.file-extension=properties", + "spring.cloud.nacos.config.contextPath=test-contextpath", + "spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties", + "spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties", + "spring.cloud.nacos.config.extension-configs[1].group=GLOBAL_GROUP", + "spring.cloud.nacos.config.shared-configs[0]=common1.properties", + "spring.cloud.nacos.config.shared-configs[1]=common2.properties", + "spring.cloud.nacos.config.accessKey=test-accessKey", + "spring.cloud.nacos.config.secretKey=test-secretKey" }, webEnvironment = NONE) +public class NacosConfigurationNewTest { + + static { + + try { + + Method method = PowerMockito.method(NacosConfigService.class, "getConfig", + String.class, String.class, long.class); + MethodProxy.proxy(method, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + if ("test-name.properties".equals(args[0]) + && "test-group".equals(args[1])) { + return "user.name=hello\nuser.age=12"; + } + + if ("test-name-dev.properties".equals(args[0]) + && "test-group".equals(args[1])) { + return "user.name=dev"; + } + + if ("ext-config-common01.properties".equals(args[0]) + && "DEFAULT_GROUP".equals(args[1])) { + return "test-ext-config1=config1\ntest-ext-config2=config1"; + } + if ("ext-config-common02.properties".equals(args[0]) + && "GLOBAL_GROUP".equals(args[1])) { + return "test-ext-config2=config2"; + } + + if ("common1.properties".equals(args[0]) + && "DEFAULT_GROUP".equals(args[1])) { + return "test-common1=common1\ntest-common2=common1"; + } + + if ("common2.properties".equals(args[0]) + && "DEFAULT_GROUP".equals(args[1])) { + return "test-common2=common2"; + } + + return ""; + } + }); + + } + catch (Exception ignore) { + ignore.printStackTrace(); + + } + } + + @Autowired + private Environment environment; + + @Autowired + private NacosPropertySourceLocator locator; + + @Autowired + private NacosConfigProperties properties; + + @Autowired + private NacosRefreshHistory refreshHistory; + + @Test + public void contextLoads() throws Exception { + + assertThat(locator).isNotNull(); + assertThat(properties).isNotNull(); + + checkoutNacosConfigServerAddr(); + checkoutNacosConfigNamespace(); + checkoutNacosConfigClusterName(); + checkoutNacosConfigAccessKey(); + checkoutNacosConfigSecrectKey(); + checkoutNacosConfigName(); + checkoutNacosConfigGroup(); + checkoutNacosConfigContextPath(); + checkoutNacosConfigFileExtension(); + checkoutNacosConfigTimeout(); + checkoutNacosConfigEncode(); + checkShareConfigs(); + checkExtensionConfigs(); + + checkoutEndpoint(); + checkoutDataLoad(); + System.out.println(properties.toString()); + } + + private void checkoutNacosConfigServerAddr() { + assertThat(properties.getServerAddr()).isEqualTo("127.0.0.1:8848"); + } + + private void checkoutNacosConfigNamespace() { + assertThat(properties.getNamespace()).isEqualTo("test-namespace"); + } + + private void checkoutNacosConfigClusterName() { + assertThat(properties.getClusterName()).isEqualTo("test-cluster"); + } + + private void checkoutNacosConfigAccessKey() { + assertThat(properties.getAccessKey()).isEqualTo("test-accessKey"); + } + + private void checkoutNacosConfigSecrectKey() { + assertThat(properties.getSecretKey()).isEqualTo("test-secretKey"); + } + + private void checkoutNacosConfigContextPath() { + assertThat(properties.getContextPath()).isEqualTo("test-contextpath"); + } + + private void checkoutNacosConfigName() { + assertThat(properties.getName()).isEqualTo("test-name"); + } + + private void checkoutNacosConfigGroup() { + assertThat(properties.getGroup()).isEqualTo("test-group"); + } + + private void checkoutNacosConfigFileExtension() { + assertThat(properties.getFileExtension()).isEqualTo("properties"); + } + + private void checkoutNacosConfigTimeout() { + assertThat(properties.getTimeout()).isEqualTo(1000); + } + + private void checkoutNacosConfigEncode() { + assertThat(properties.getEncode()).isEqualTo("utf-8"); + } + + private void checkoutDataLoad() { + assertThat(environment.getProperty("user.name")).isEqualTo("dev"); + assertThat(environment.getProperty("user.age")).isEqualTo("12"); + } + + private void checkoutEndpoint() throws Exception { + NacosConfigEndpoint nacosConfigEndpoint = new NacosConfigEndpoint(properties, + refreshHistory); + Map map = nacosConfigEndpoint.invoke(); + assertThat(map.get("NacosConfigProperties")).isEqualTo(properties); + assertThat(map.get("RefreshHistory")).isEqualTo(refreshHistory.getRecords()); + } + + private void checkShareConfigs() throws Exception { + assertThat(properties.getSharedConfigs() != null); + assertThat(properties.getSharedConfigs()).contains( + new Config("common1.properties"), new Config("common2.properties")); + } + + private void checkExtensionConfigs() throws Exception { + assertThat(properties.getExtensionConfigs() != null); + assertThat(properties.getExtensionConfigs()).contains( + new Config("ext-config-common01.properties"), + new Config("ext-config-common02.properties", "GLOBAL_GROUP")); + } + + @Configuration + @EnableAutoConfiguration + @ImportAutoConfiguration({ NacosConfigEndpointAutoConfiguration.class, + NacosConfigAutoConfiguration.class, NacosConfigBootstrapConfiguration.class }) + public static class TestConfig { + + } + +}