diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java index e279d9cbe..79b2ac6e7 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java @@ -16,15 +16,8 @@ package org.springframework.cloud.alibaba.nacos; -import java.util.Arrays; -import java.util.Objects; -import java.util.Properties; - -import javax.annotation.PostConstruct; - import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -32,20 +25,20 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.env.Environment; -import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY; -import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME; -import static com.alibaba.nacos.api.PropertyKeyConst.CONTEXT_PATH; -import static com.alibaba.nacos.api.PropertyKeyConst.ENCODE; -import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT; -import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE; -import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY; -import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; +import javax.annotation.PostConstruct; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +import static com.alibaba.nacos.api.PropertyKeyConst.*; /** * nacos properties * * @author leijuan * @author xiaojing + * @author pbting */ @ConfigurationProperties("spring.cloud.nacos.config") public class NacosConfigProperties { @@ -118,6 +111,20 @@ public class NacosConfigProperties { private String[] activeProfiles; + /** + * the dataids for configurable multiple shared configurations , multiple separated by + * commas . + */ + private String sharedDataids; + /** + * refreshable dataids , multiple separated by commas . + */ + private String refreshableDataids; + /** + * a set of extended configurations . + */ + private List extConfig; + private ConfigService configService; @Autowired @@ -234,6 +241,69 @@ public class NacosConfigProperties { return activeProfiles; } + public String getSharedDataids() { + return sharedDataids; + } + + public void setSharedDataids(String sharedDataids) { + this.sharedDataids = sharedDataids; + } + + public String getRefreshableDataids() { + return refreshableDataids; + } + + public void setRefreshableDataids(String refreshableDataids) { + this.refreshableDataids = refreshableDataids; + } + + public List getExtConfig() { + return extConfig; + } + + public void setExtConfig(List extConfig) { + this.extConfig = extConfig; + } + + public static class Config { + /** + * the data id of extended configuration + */ + private String dataId; + /** + * the group of extended configuration, the default value is DEFAULT_GROUP + */ + private String group = "DEFAULT_GROUP"; + /** + * whether to support dynamic refresh, the default does not support . + */ + private boolean refresh = false; + + 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 isRefresh() { + return refresh; + } + + public void setRefresh(boolean refresh) { + this.refresh = refresh; + } + } + @Override public String toString() { return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\'' @@ -243,7 +313,11 @@ public class NacosConfigProperties { + ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\'' + ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath + '\'' + ", clusterName='" + clusterName + '\'' + ", name='" + name + '\'' - + ", activeProfiles=" + Arrays.toString(activeProfiles) + '}'; + + ", activeProfiles=" + Arrays.toString(activeProfiles) + + ", sharedDataids='" + sharedDataids + '\'' + ", refreshableDataids='" + + refreshableDataids + '\'' + ", extConfig=" + extConfig + + ", configService=" + configService + ", environment=" + environment + + '}'; } public ConfigService configServiceInstance() { diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySource.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySource.java index 7d9d9a0d2..0066d17e2 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySource.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySource.java @@ -16,16 +16,22 @@ package org.springframework.cloud.alibaba.nacos.client; +import org.springframework.core.env.MapPropertySource; + import java.util.Date; import java.util.Map; -import org.springframework.core.env.MapPropertySource; - /** * @author xiaojing + * @author pbting */ public class NacosPropertySource extends MapPropertySource { + /** + * Nacos Group + */ + private final String group; + /** * Nacos dataID */ @@ -36,11 +42,22 @@ public class NacosPropertySource extends MapPropertySource { */ private final Date timestamp; + /** + * Whether to support dynamic refresh for this Property Source + */ + private final boolean isRefreshable; - NacosPropertySource(String dataId, Map source, Date timestamp) { + NacosPropertySource(String group, String dataId, Map source, + Date timestamp, boolean isRefreshable) { super(dataId, source); + this.group = group; this.dataId = dataId; this.timestamp = timestamp; + this.isRefreshable = isRefreshable; + } + + public String getGroup() { + return this.group; } public String getDataId() { @@ -51,4 +68,7 @@ public class NacosPropertySource extends MapPropertySource { return timestamp; } + public boolean isRefreshable() { + return isRefreshable; + } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceBuilder.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceBuilder.java index 16bf97d09..589a3bac2 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceBuilder.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceBuilder.java @@ -16,24 +16,20 @@ package org.springframework.cloud.alibaba.nacos.client; -import java.io.StringReader; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ByteArrayResource; import org.springframework.util.StringUtils; +import java.io.StringReader; +import java.util.*; + /** * @author xiaojing + * @author pbting */ public class NacosPropertySourceBuilder { @@ -71,12 +67,14 @@ public class NacosPropertySourceBuilder { * @param dataId Nacos dataId * @param group Nacos group */ - NacosPropertySource build(String dataId, String group, String fileExtension) { + NacosPropertySource build(String dataId, String group, String fileExtension, + boolean isRefreshable) { Properties p = loadNacosData(dataId, group, fileExtension); if (p == null) { return null; } - return new NacosPropertySource(dataId, propertiesToMap(p), new Date()); + return new NacosPropertySource(group, dataId, propertiesToMap(p), new Date(), + isRefreshable); } private Properties loadNacosData(String dataId, String group, String fileExtension) { diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java index 304fc5672..678e559e5 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java @@ -16,6 +16,7 @@ package org.springframework.cloud.alibaba.nacos.client; +import com.alibaba.nacos.api.config.ConfigService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,10 +28,12 @@ import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.util.StringUtils; -import com.alibaba.nacos.api.config.ConfigService; +import java.util.Arrays; +import java.util.List; /** * @author xiaojing + * @author pbting */ @Order(0) public class NacosPropertySourceLocator implements PropertySourceLocator { @@ -40,6 +43,9 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { private static final String NACOS_PROPERTY_SOURCE_NAME = "NACOS"; private static final String SEP1 = "-"; private static final String DOT = "."; + private static final String SHARED_CONFIG_SEPRATOR_CHAR = "[,]"; + private static final List SUPPORT_FILE_EXTENSION = Arrays.asList("properties", + "yaml", "yml"); @Autowired private NacosConfigProperties nacosConfigProperties; @@ -73,29 +79,113 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); + loadSharedConfiguration(composite); + loadExtConfiguration(composite); loadApplicationConfiguration(composite, nacosGroup, dataIdPrefix, fileExtension); return composite; } + private void loadSharedConfiguration( + CompositePropertySource compositePropertySource) { + String sharedDataIds = nacosConfigProperties.getSharedDataids(); + String refreshDataIds = nacosConfigProperties.getRefreshableDataids(); + if (sharedDataIds == null || sharedDataIds.trim().length() == 0) { + return; + } + String[] sharedDataIdArry = sharedDataIds.split(SHARED_CONFIG_SEPRATOR_CHAR); + checkDataIdFileExtension(sharedDataIdArry); + for (int i = 0; i < sharedDataIdArry.length; i++) { + String dataId = sharedDataIdArry[i]; + String fileExtension = dataId.substring(dataId.lastIndexOf(".") + 1); + boolean isRefreshable = checkDataIdIsRefreshbable(refreshDataIds, + sharedDataIdArry[i]); + loadNacosDataIfPresent(compositePropertySource, dataId, "DEFAULT_GROUP", + fileExtension, isRefreshable); + } + } + + private void loadExtConfiguration(CompositePropertySource compositePropertySource) { + if (nacosConfigProperties.getExtConfig() == null + || nacosConfigProperties.getExtConfig().isEmpty()) { + return; + } + List extConfigs = nacosConfigProperties + .getExtConfig(); + checkExtConfiguration(extConfigs); + for (NacosConfigProperties.Config config : extConfigs) { + String dataId = config.getDataId(); + String fileExtension = dataId.substring(dataId.lastIndexOf(".") + 1); + loadNacosDataIfPresent(compositePropertySource, dataId, config.getGroup(), + fileExtension, config.isRefresh()); + } + } + + 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); + } + private void loadApplicationConfiguration( CompositePropertySource compositePropertySource, String nacosGroup, String dataIdPrefix, String fileExtension) { loadNacosDataIfPresent(compositePropertySource, - dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension); + dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true); for (String profile : nacosConfigProperties.getActiveProfiles()) { String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension; loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, - fileExtension); + fileExtension, true); } } private void loadNacosDataIfPresent(final CompositePropertySource composite, - final String dataId, final String group, String fileExtension) { + final String dataId, final String group, String fileExtension, + boolean isRefreshable) { NacosPropertySource ps = nacosPropertySourceBuilder.build(dataId, group, - fileExtension); + fileExtension, isRefreshable); if (ps != null) { composite.addFirstPropertySource(ps); } } + + private static void checkDataIdFileExtension(String[] sharedDataIdArry) { + StringBuilder stringBuilder = new StringBuilder(); + outline: for (int i = 0; i < sharedDataIdArry.length; i++) { + for (String fileExtension : SUPPORT_FILE_EXTENSION) { + if (sharedDataIdArry[i].indexOf(fileExtension) > 0) { + continue outline; + } + } + stringBuilder.append(sharedDataIdArry[i] + ","); + } + if (stringBuilder.length() > 0) { + String result = stringBuilder.substring(0, stringBuilder.length() - 1); + throw new IllegalStateException(String.format( + "[%s] must contains file extension with properties|yaml|yml", + result)); + } + } + + private boolean checkDataIdIsRefreshbable(String refreshDataIds, + String sharedDataId) { + if (refreshDataIds == null || "".equals(refreshDataIds)) { + return false; + } + String[] refreshDataIdArry = refreshDataIds.split(SHARED_CONFIG_SEPRATOR_CHAR); + for (String refreshDataId : refreshDataIdArry) { + if (refreshDataId.equals(sharedDataId)) { + return true; + } + } + return false; + } } diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/refresh/NacosContextRefresher.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/refresh/NacosContextRefresher.java index f38643ce0..6b7c57ff8 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/refresh/NacosContextRefresher.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/refresh/NacosContextRefresher.java @@ -16,14 +16,9 @@ package org.springframework.cloud.alibaba.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 com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import com.alibaba.nacos.api.exception.NacosException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -34,9 +29,13 @@ import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.context.ApplicationListener; import org.springframework.util.StringUtils; -import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.Listener; -import com.alibaba.nacos.api.exception.NacosException; +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; /** * On application start up, NacosContextRefresher add nacos listeners to all application @@ -44,6 +43,7 @@ import com.alibaba.nacos.api.exception.NacosException; * configurations. * * @author juven.xuxb + * @author pbting */ public class NacosContextRefresher implements ApplicationListener { @@ -61,7 +61,7 @@ public class NacosContextRefresher implements ApplicationListener listenerMap = new ConcurrentHashMap<>(16); + private Map listenerMap = new ConcurrentHashMap<>(16); public NacosContextRefresher(ContextRefresher contextRefresher, NacosConfigProperties properties, NacosRefreshProperties refreshProperties, @@ -85,27 +85,35 @@ public class NacosContextRefresher implements ApplicationListener { diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java index d4ae7f1de..70f33f976 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java @@ -16,14 +16,14 @@ package org.springframework.cloud.alibaba.nacos.ribbon; -import java.util.Map; - import com.alibaba.nacos.api.naming.pojo.Instance; - import com.netflix.loadbalancer.Server; +import java.util.Map; + /** * @author xiaojing + * @author pbting */ public class NacosServer extends Server { diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 2ea041b08..27f753ffb 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,8 +1,16 @@ -{"properties": [ +{ + "properties": [ { "name": "spring.cloud.nacos.discovery.service", "type": "java.lang.String", "defaultValue": "${spring.application.name}", "description": "the service name to register, default value is ${spring.application.name}." + }, + { + "name": "spring.cloud.nacos.discovery.namingLoadCacheAtStart", + "type": "java.lang.Boolean", + "defaultValue": "false", + "description": "naming load from local cache at application start ." } -]} + ] +} diff --git a/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java b/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java new file mode 100644 index 000000000..6b5a68bdf --- /dev/null +++ b/spring-cloud-alicloud-context/src/main/java/org/springframework/cloud/alicloud/context/nacos/NacosParameterInitListener.java @@ -0,0 +1,44 @@ +package org.springframework.cloud.alicloud.context.nacos; + +import com.alibaba.cloud.context.edas.EdasChangeOrderConfiguration; +import com.alibaba.cloud.context.edas.EdasChangeOrderConfigurationFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.context.ApplicationListener; + +/** + * A listener that prepare initialize the nacos configuration for aliyun acm + * + * @author pbting + */ +public class NacosParameterInitListener + implements ApplicationListener { + private static final Logger log = LoggerFactory + .getLogger(NacosParameterInitListener.class); + + @Override + public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { + preparedNacosConfiguration(); + } + + private void preparedNacosConfiguration() { + EdasChangeOrderConfiguration edasChangeOrderConfiguration = EdasChangeOrderConfigurationFactory + .buildEdasChangeOrderConfiguration(); + log.info("Initialize Nacos Parameter from edas change order,is edas managed {}.", + edasChangeOrderConfiguration.isEdasManaged()); + if (!edasChangeOrderConfiguration.isEdasManaged()) { + return; + } + // initialize nacos configuration + System.getProperties().setProperty("spring.cloud.nacos.config.server-addr", ""); + System.getProperties().setProperty("spring.cloud.nacos.config.endpoint", + edasChangeOrderConfiguration.getAddressServerDomain()); + System.getProperties().setProperty("spring.cloud.nacos.config.namespace", + edasChangeOrderConfiguration.getTenantId()); + System.getProperties().setProperty("spring.cloud.nacos.config.access-key", + edasChangeOrderConfiguration.getDauthAccessKey()); + System.getProperties().setProperty("spring.cloud.nacos.config.secret-key", + edasChangeOrderConfiguration.getDauthSecretKey()); + } +} \ No newline at end of file diff --git a/spring-cloud-alicloud-context/src/main/resources/META-INF/spring.factories b/spring-cloud-alicloud-context/src/main/resources/META-INF/spring.factories index e10dcb959..b5ed9c32a 100644 --- a/spring-cloud-alicloud-context/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alicloud-context/src/main/resources/META-INF/spring.factories @@ -6,4 +6,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.alicloud.context.ans.AnsContextAutoConfiguration,\ org.springframework.cloud.alicloud.context.oss.OssContextAutoConfiguration org.springframework.context.ApplicationListener=\ - org.springframework.cloud.alicloud.context.ans.AnsContextApplicationListener \ No newline at end of file + org.springframework.cloud.alicloud.context.ans.AnsContextApplicationListener,\ + org.springframework.cloud.alicloud.context.nacos.NacosParameterInitListener \ No newline at end of file