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..37f9ce967 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,22 @@ 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,10 +243,66 @@ 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 { + private String dataId; + private String group = "DEFAULT_GROUP"; + 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 + '\'' - + ", encode='" + encode + '\'' + ", group='" + group + '\'' + ", prefix='" + + ", encode='" + encode + '\'' + ", group='" + group + '\'' + + ", sharedDataids='" + this.sharedDataids + '\'' + + ", refreshableDataids='" + this.refreshableDataids + '\'' + ", prefix='" + prefix + '\'' + ", fileExtension='" + fileExtension + '\'' + ", timeout=" + timeout + ", endpoint='" + endpoint + '\'' + ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\'' 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..a6d697839 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; @@ -26,8 +27,8 @@ import org.springframework.core.env.CompositePropertySource; 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 @@ -40,6 +41,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 +77,123 @@ 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 88d237a0b..448f6dd16 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,13 +85,17 @@ public class NacosContextRefresher implements ApplicationListener new Listener() { @Override @@ -101,9 +105,10 @@ public class NacosContextRefresher 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