From 1186028ab4a0de5c3aaf4559792371d0b91e5f6e Mon Sep 17 00:00:00 2001 From: onewe Date: Tue, 9 Aug 2022 17:30:05 +0800 Subject: [PATCH] [ISSUE #8622] add the scope isolation strategy - add setProperty,addProperties and containsKey methods - remove PropertySourceSearch --- .../nacos/client/constant/Constants.java | 2 +- .../client/env/AbstractPropertySource.java | 15 ++ .../alibaba/nacos/client/env/ApplyScope.java | 40 ++++ .../env/DefaultSettingPropertySource.java | 19 +- .../client/env/JvmArgsPropertySource.java | 18 +- .../nacos/client/env/NacosEnvironment.java | 27 +++ .../client/env/NacosEnvironmentFactory.java | 68 ------ .../alibaba/nacos/client/env/NacosEnvs.java | 82 ++++++-- .../client/env/PropertiesPropertySource.java | 92 +++++++- .../client/env/PropertySourceSearch.java | 119 ----------- .../client/env/SearchableEnvironment.java | 178 +++++++++++++++- .../client/env/SystemEnvPropertySource.java | 17 +- .../env/NacosEnvironmentFactoryTest.java | 50 ----- .../nacos/client/env/NacosEnvsTest.java | 199 ++++++++++-------- 14 files changed, 551 insertions(+), 375 deletions(-) create mode 100644 client/src/main/java/com/alibaba/nacos/client/env/ApplyScope.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java delete mode 100644 client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java diff --git a/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java b/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java index 53a35d574e9..f38cc7db7ab 100644 --- a/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java +++ b/client/src/main/java/com/alibaba/nacos/client/constant/Constants.java @@ -35,7 +35,7 @@ public static class SysEnv { public static final String JM_SNAPSHOT_PATH = "JM.SNAPSHOT.PATH"; - public static final String NACOS_ENVS_SEARCH = "nacos.envs.search"; + public static final String NACOS_ENV_FIRST = "nacos.env.first"; } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java index 3b34951d672..779580a1895 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.client.env; +import java.util.Properties; + abstract class AbstractPropertySource { /** @@ -31,4 +33,17 @@ abstract class AbstractPropertySource { */ abstract String getProperty(String key); + /** + * Tests if the specified object is a key in this propertySource. + * @param key key – possible key + * @return true if and only if the specified object is a key in this propertySource, false otherwise. + */ + abstract boolean containsKey(String key); + + /** + * to properties. + * @return properties + */ + abstract Properties asProperties(); + } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/ApplyScope.java b/client/src/main/java/com/alibaba/nacos/client/env/ApplyScope.java new file mode 100644 index 00000000000..7d7d312c468 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/env/ApplyScope.java @@ -0,0 +1,40 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.nacos.client.env; + +/** + * environment apply scope. + * @author onewe + */ +public enum ApplyScope { + /** + * global scope. + */ + GLOBAL, + /** + * config module scope. + */ + CONFIG, + /** + * naming module scope. + */ + NAMING, + /** + * naming maintain module scope. + */ + NAMING_MAINTAIN +} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java index 6539c698969..6990e0e4721 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java @@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory; import java.io.InputStream; -import java.net.URL; import java.util.Properties; class DefaultSettingPropertySource extends AbstractPropertySource { @@ -33,12 +32,10 @@ class DefaultSettingPropertySource extends AbstractPropertySource { private final Properties defaultSetting = new Properties(); DefaultSettingPropertySource() { - try { - final URL resourceUrl = ResourceUtils.getResourceUrl(DEFAULT_SETTING_PATH); - final InputStream inputStream = resourceUrl.openStream(); + try (final InputStream inputStream = ResourceUtils.getResourceUrl(DEFAULT_SETTING_PATH).openStream()) { defaultSetting.load(inputStream); } catch (Exception e) { - LOGGER.warn("load default setting failed"); + LOGGER.error("load default setting failed", e); } } @@ -51,4 +48,16 @@ SourceType getType() { String getProperty(String key) { return defaultSetting.getProperty(key); } + + @Override + boolean containsKey(String key) { + return defaultSetting.containsKey(key); + } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(defaultSetting); + return properties; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java index 5c4475c05f5..bd392bca076 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java @@ -20,11 +20,7 @@ class JvmArgsPropertySource extends AbstractPropertySource { - private final Properties properties; - - JvmArgsPropertySource() { - this.properties = System.getProperties(); - } + private final Properties properties = new Properties(System.getProperties()); @Override SourceType getType() { @@ -35,4 +31,16 @@ SourceType getType() { String getProperty(String key) { return properties.getProperty(key); } + + @Override + boolean containsKey(String key) { + return properties.containsKey(key); + } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(this.properties); + return properties; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java index 14269fc6dd7..5d0a068a1a2 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.client.env; +import java.util.Properties; + /** * nacos env interface. * @@ -87,4 +89,29 @@ public interface NacosEnvironment { */ Long getLong(String key, Long defaultValue); + /** + * set property. + * @param key key + * @param value value + */ + void setProperty(String key, String value); + + /** + * add properties. + * @param properties properties + */ + void addProperties(Properties properties); + + /** + * Tests if the specified object is a key in this environment. + * @param key key – possible key + * @return true if and only if the specified object is a key in this environment, false otherwise. + */ + boolean containsKey(String key); + + /** + * get properties from environment. + * @return properties + */ + Properties asProperties(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java deleted file mode 100644 index 74e65a461ae..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironmentFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 1999-2022 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.nacos.client.env; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Properties; - -class NacosEnvironmentFactory { - - /** - * create nacos environment. - * @return NacosEnvironment's proxy object, it contains a SearchableEnvironment object. - * @see SearchableEnvironment - */ - static NacosEnvironment createEnvironment() { - - return (NacosEnvironment) Proxy.newProxyInstance(NacosEnvironmentFactory.class.getClassLoader(), new Class[] {NacosEnvironment.class}, - new NacosEnvironmentDelegate() { - volatile NacosEnvironment environment; - - @Override - public void init(Properties properties) { - if (environment == null) { - synchronized (NacosEnvironmentFactory.class) { - if (environment == null) { - environment = new SearchableEnvironment(properties); - } - } - } - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (environment == null) { - throw new IllegalStateException( - "Nacos environment doesn't init, please call NacosEnvs#init method then try it again."); - } - return method.invoke(environment, args); - } - }); - } - - interface NacosEnvironmentDelegate extends InvocationHandler { - - /** - * init environment. - * @param properties user customize properties - */ - void init(Properties properties); - } - -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java index 8510e575a57..6eec9b555e9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java @@ -16,29 +16,46 @@ package com.alibaba.nacos.client.env; -import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; /** * environment utils. + * * @author onewe */ public class NacosEnvs { - private static final NacosEnvironment ENVIRONMENT = NacosEnvironmentFactory.createEnvironment(); + private static final Map ENVIRONMENT_MAP = new HashMap<>(4); - /** - * init environment. - * @param properties properties - */ - public static void init(Properties properties) { - NacosEnvironmentFactory.NacosEnvironmentDelegate warrper = (NacosEnvironmentFactory.NacosEnvironmentDelegate) Proxy.getInvocationHandler( - ENVIRONMENT); - warrper.init(properties); + static { + ENVIRONMENT_MAP.put(ApplyScope.GLOBAL, new SearchableEnvironment()); + ENVIRONMENT_MAP.put(ApplyScope.CONFIG, new SearchableEnvironment() { + @Override + protected ApplyScope getScope() { + return ApplyScope.CONFIG; + } + }); + + ENVIRONMENT_MAP.put(ApplyScope.NAMING, new SearchableEnvironment() { + @Override + protected ApplyScope getScope() { + return ApplyScope.NAMING; + } + }); + + ENVIRONMENT_MAP.put(ApplyScope.NAMING_MAINTAIN, new SearchableEnvironment() { + @Override + protected ApplyScope getScope() { + return ApplyScope.NAMING_MAINTAIN; + } + }); + } public static String getProperty(String key, String defaultValue) { - return ENVIRONMENT.getProperty(key, defaultValue); + return apply(ApplyScope.GLOBAL).getProperty(key, defaultValue); } /** @@ -48,7 +65,7 @@ public static String getProperty(String key, String defaultValue) { * @return string value or null. */ public static String getProperty(String key) { - return ENVIRONMENT.getProperty(key); + return apply(ApplyScope.GLOBAL).getProperty(key); } /** @@ -58,7 +75,7 @@ public static String getProperty(String key) { * @return boolean value or null. */ public static Boolean getBoolean(String key) { - return ENVIRONMENT.getBoolean(key); + return apply(ApplyScope.GLOBAL).getBoolean(key); } /** @@ -69,7 +86,7 @@ public static Boolean getBoolean(String key) { * @return boolean value or defaultValue. */ public static Boolean getBoolean(String key, Boolean defaultValue) { - return ENVIRONMENT.getBoolean(key, defaultValue); + return apply(ApplyScope.GLOBAL).getBoolean(key, defaultValue); } /** @@ -79,7 +96,7 @@ public static Boolean getBoolean(String key, Boolean defaultValue) { * @return integer value or null */ public static Integer getInteger(String key) { - return ENVIRONMENT.getInteger(key); + return apply(ApplyScope.GLOBAL).getInteger(key); } /** @@ -90,7 +107,7 @@ public static Integer getInteger(String key) { * @return integer value or default value */ public static Integer getInteger(String key, Integer defaultValue) { - return ENVIRONMENT.getInteger(key, defaultValue); + return apply(ApplyScope.GLOBAL).getInteger(key, defaultValue); } /** @@ -100,7 +117,7 @@ public static Integer getInteger(String key, Integer defaultValue) { * @return long value or null */ public static Long getLong(String key) { - return ENVIRONMENT.getLong(key); + return apply(ApplyScope.GLOBAL).getLong(key); } /** @@ -111,6 +128,35 @@ public static Long getLong(String key) { * @return long value or default value */ public static Long getLong(String key, Long defaultValue) { - return ENVIRONMENT.getLong(key, defaultValue); + return apply(ApplyScope.GLOBAL).getLong(key, defaultValue); } + + public static void setProperty(String key, String value) { + apply(ApplyScope.GLOBAL).setProperty(key, value); + } + + public static void addProperties(Properties properties) { + apply(ApplyScope.GLOBAL).addProperties(properties); + } + + public static boolean containsKey(String key) { + return apply(ApplyScope.GLOBAL).containsKey(key); + } + + public static Properties asProperties() { + return apply(ApplyScope.GLOBAL).asProperties(); + } + + /** + * apply scope to environment. + * @param applyScope scope + * @return NacosEnvironment + */ + public static NacosEnvironment apply(ApplyScope applyScope) { + if (applyScope == null) { + return ENVIRONMENT_MAP.get(ApplyScope.GLOBAL); + } + return ENVIRONMENT_MAP.get(applyScope); + } + } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java index 991f83a4d81..bd653641331 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java @@ -16,14 +16,17 @@ package com.alibaba.nacos.client.env; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; class PropertiesPropertySource extends AbstractPropertySource { - private final Properties properties; + private final Map propertiesMap = new HashMap<>(8); - PropertiesPropertySource(Properties properties) { - this.properties = properties; + public PropertiesPropertySource() { + Properties properties = new Properties(); + propertiesMap.put(ApplyScope.GLOBAL, properties); } @Override @@ -33,6 +36,87 @@ SourceType getType() { @Override String getProperty(String key) { - return properties.getProperty(key); + return propertiesMap.get(ApplyScope.GLOBAL).getProperty(key); + } + + String getProperty(ApplyScope scope, String key) { + if (scope == null || ApplyScope.GLOBAL.equals(scope)) { + return propertiesMap.get(ApplyScope.GLOBAL).getProperty(key); + } + String value; + final Properties properties = propertiesMap.get(scope); + if (properties == null) { + value = null; + } else { + value = properties.getProperty(key); + } + + if (value == null) { + value = propertiesMap.get(ApplyScope.GLOBAL).getProperty(key); + } + return value; + } + + @Override + boolean containsKey(String key) { + return propertiesMap.get(ApplyScope.GLOBAL).containsKey(key); + } + + boolean containsKey(ApplyScope scope, String key) { + if (scope == null || ApplyScope.GLOBAL.equals(scope)) { + return propertiesMap.get(ApplyScope.GLOBAL).containsKey(key); + } + boolean containing; + final Properties properties = propertiesMap.get(scope); + if (properties == null) { + containing = false; + } else { + containing = properties.containsKey(key); + } + if (!containing) { + containing = propertiesMap.get(ApplyScope.GLOBAL).containsKey(key); + } + return containing; + } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(propertiesMap.get(ApplyScope.GLOBAL)); + return properties; + } + + Properties asProperties(ApplyScope applyScope) { + if (applyScope == null || ApplyScope.GLOBAL.equals(applyScope)) { + Properties properties = new Properties(); + properties.putAll(propertiesMap.get(ApplyScope.GLOBAL)); + return properties; + } + Properties properties = new Properties(); + properties.putAll(propertiesMap.get(ApplyScope.GLOBAL)); + + final Properties applyScopeProperties = propertiesMap.get(applyScope); + if (applyScopeProperties != null) { + properties.putAll(applyScopeProperties); + } + + return properties; + } + + synchronized void setProperty(ApplyScope scope, String key, String value) { + if (scope == null) { + scope = ApplyScope.GLOBAL; + } + final Properties properties = this.propertiesMap.computeIfAbsent(scope, s -> new Properties()); + properties.setProperty(key, value); + } + + synchronized void addProperties(ApplyScope scope, Properties properties) { + if (scope == null) { + scope = ApplyScope.GLOBAL; + } + final Properties existProperties = this.propertiesMap.computeIfAbsent(scope, s -> new Properties()); + + existProperties.putAll(properties); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java b/client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java deleted file mode 100644 index e73dc995af0..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/PropertySourceSearch.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 1999-2022 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.nacos.client.env; - -import com.alibaba.nacos.client.constant.Constants; -import com.alibaba.nacos.client.env.convert.CompositeConverter; -import com.alibaba.nacos.common.utils.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.stream.Collectors; - -class PropertySourceSearch { - - private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourceSearch.class); - - private static final List DEFAULT_ORDER = Arrays.asList(SourceType.PROPERTIES, SourceType.JVM, - SourceType.SYS); - - private final List propertySources; - - private final CompositeConverter converter; - - private PropertySourceSearch(List propertySources) { - this.propertySources = propertySources; - this.propertySources.add(new DefaultSettingPropertySource()); - this.converter = new CompositeConverter(); - } - - static PropertySourceSearch build(Properties properties) { - if (properties == null) { - properties = new Properties(); - } - PropertiesPropertySource customizePropertySource = new PropertiesPropertySource(properties); - JvmArgsPropertySource jvmArgsPropertySource = new JvmArgsPropertySource(); - SystemEnvPropertySource systemEnvPropertySource = new SystemEnvPropertySource(); - - String searchPattern = jvmArgsPropertySource.getProperty(Constants.SysEnv.NACOS_ENVS_SEARCH); - if (StringUtils.isBlank(searchPattern)) { - searchPattern = systemEnvPropertySource.getProperty(Constants.SysEnv.NACOS_ENVS_SEARCH); - } - - return resolve(searchPattern, customizePropertySource, jvmArgsPropertySource, systemEnvPropertySource); - } - - private static PropertySourceSearch resolve(String pattern, AbstractPropertySource... propertySources) { - - if (StringUtils.isBlank(pattern)) { - return createPropertySourceSearchWithDefaultOrder(propertySources); - } - - try { - final SourceType sourceType = SourceType.valueOf(pattern.toUpperCase()); - return createPropertySourceSearchByFirstType(sourceType, propertySources); - } catch (Exception e) { - LOGGER.error("first source type parse error, it will be use default order!"); - return createPropertySourceSearchWithDefaultOrder(propertySources); - } - } - - private static PropertySourceSearch createPropertySourceSearchWithDefaultOrder(AbstractPropertySource... propertySources) { - final Map sourceMap = Arrays.stream(propertySources) - .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); - final List collect = DEFAULT_ORDER.stream().map(sourceMap::get).collect(Collectors.toList()); - return new PropertySourceSearch(collect); - } - - private static PropertySourceSearch createPropertySourceSearchByFirstType(SourceType firstType, - AbstractPropertySource... propertySources) { - - List tempList = new ArrayList<>(4); - tempList.add(firstType); - - final Map sourceMap = Arrays.stream(propertySources) - .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); - final List collect = DEFAULT_ORDER.stream().filter(sourceType -> !sourceType.equals(firstType)) - .collect(() -> tempList, List::add, List::addAll).stream().map(sourceMap::get) - .collect(Collectors.toList()); - - return new PropertySourceSearch(collect); - } - - Optional search(String key, Class targetType) { - if (targetType == null) { - throw new IllegalArgumentException("target type must be not null!"); - } - - for (AbstractPropertySource propertySource : propertySources) { - final String value = propertySource.getProperty(key); - if (value != null) { - if (String.class.isAssignableFrom(targetType)) { - return (Optional) Optional.of(value); - } - return Optional.ofNullable(converter.convert(value, targetType)); - } - } - return Optional.empty(); - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java b/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java index 7dbb6bc5c47..478c133afc7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java @@ -16,7 +16,21 @@ package com.alibaba.nacos.client.env; +import com.alibaba.nacos.client.constant.Constants; +import com.alibaba.nacos.client.env.convert.CompositeConverter; +import com.alibaba.nacos.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.Properties; +import java.util.stream.Collectors; /** * Searchable environment. @@ -25,10 +39,82 @@ */ class SearchableEnvironment implements NacosEnvironment { - private final PropertySourceSearch sourceSearch; + private static final Logger LOGGER = LoggerFactory.getLogger(SearchableEnvironment.class); + + private static final PropertiesPropertySource PROPERTIES_PROPERTY_SOURCE = new PropertiesPropertySource(); + + private static final JvmArgsPropertySource JVM_ARGS_PROPERTY_SOURCE = new JvmArgsPropertySource(); + + private static final SystemEnvPropertySource SYSTEM_ENV_PROPERTY_SOURCE = new SystemEnvPropertySource(); + + private static final List DEFAULT_ORDER = Arrays.asList(SourceType.PROPERTIES, SourceType.JVM, + SourceType.SYS); + + private static final List PROPERTY_SOURCES; + + private static final CompositeConverter CONVERTER = new CompositeConverter(); + + static { + String searchPattern = JVM_ARGS_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); + if (StringUtils.isBlank(searchPattern)) { + searchPattern = SYSTEM_ENV_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); + } + final List sourceList = resolvePattern(searchPattern, PROPERTIES_PROPERTY_SOURCE, + JVM_ARGS_PROPERTY_SOURCE, SYSTEM_ENV_PROPERTY_SOURCE); + sourceList.add(new DefaultSettingPropertySource()); + + PROPERTY_SOURCES = sourceList; + } + + private static List resolvePattern(String pattern, + AbstractPropertySource... propertySources) { + + if (StringUtils.isBlank(pattern)) { + return sortPropertySourceDefaultOrder(propertySources); + } + + try { + final SourceType sourceType = SourceType.valueOf(pattern.toUpperCase()); + if (SourceType.DEFAULT_SETTING.equals(sourceType)) { + return sortPropertySourceDefaultOrder(propertySources); + } + return sortPropertySource(sourceType, propertySources); + } catch (Exception e) { + LOGGER.error("first source type parse error, it will be used default order!"); + return sortPropertySourceDefaultOrder(propertySources); + } + } + + private static List sortPropertySourceDefaultOrder( + AbstractPropertySource... propertySources) { + final Map sourceMap = Arrays.stream(propertySources) + .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); + final List collect = DEFAULT_ORDER.stream().map(sourceMap::get) + .collect(Collectors.toList()); + LOGGER.info("environment search order:PROPERTIES->JVM->SYS->DEFAULT_SETTING"); + return collect; + } - SearchableEnvironment(Properties properties) { - this.sourceSearch = PropertySourceSearch.build(properties); + private static List sortPropertySource(SourceType firstType, + AbstractPropertySource... propertySources) { + List tempList = new ArrayList<>(4); + tempList.add(firstType); + + final Map sourceMap = Arrays.stream(propertySources) + .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); + final List collect = DEFAULT_ORDER.stream() + .filter(sourceType -> !sourceType.equals(firstType)).collect(() -> tempList, List::add, List::addAll) + .stream().map(sourceMap::get).filter(Objects::nonNull).collect(Collectors.toList()); + + StringBuilder orderInfo = new StringBuilder("environment search order:"); + for (AbstractPropertySource abstractPropertySource : collect) { + orderInfo.append(abstractPropertySource.getType().toString()).append("->"); + } + orderInfo.append("DEFAULT_SETTING"); + + LOGGER.info(orderInfo.toString()); + + return collect; } @Override @@ -38,7 +124,7 @@ public String getProperty(String key) { @Override public String getProperty(String key, String defaultValue) { - return sourceSearch.search(key, String.class).orElse(defaultValue); + return this.search(this.getScope(), key, String.class).orElse(defaultValue); } @Override @@ -48,7 +134,7 @@ public Boolean getBoolean(String key) { @Override public Boolean getBoolean(String key, Boolean defaultValue) { - return sourceSearch.search(key, Boolean.class).orElse(defaultValue); + return this.search(this.getScope(), key, Boolean.class).orElse(defaultValue); } @Override @@ -58,7 +144,7 @@ public Integer getInteger(String key) { @Override public Integer getInteger(String key, Integer defaultValue) { - return sourceSearch.search(key, Integer.class).orElse(defaultValue); + return this.search(this.getScope(), key, Integer.class).orElse(defaultValue); } @Override @@ -68,7 +154,85 @@ public Long getLong(String key) { @Override public Long getLong(String key, Long defaultValue) { - return sourceSearch.search(key, Long.class).orElse(defaultValue); + return this.search(this.getScope(), key, Long.class).orElse(defaultValue); + } + + @Override + public void setProperty(String key, String value) { + PROPERTIES_PROPERTY_SOURCE.setProperty(this.getScope(), key, value); + } + + @Override + public void addProperties(Properties properties) { + PROPERTIES_PROPERTY_SOURCE.addProperties(this.getScope(), properties); + } + + @Override + public Properties asProperties() { + Properties properties = new Properties(); + final ListIterator iterator = PROPERTY_SOURCES.listIterator( + PROPERTY_SOURCES.size()); + while (iterator.hasPrevious()) { + final AbstractPropertySource previous = iterator.previous(); + if (previous instanceof PropertiesPropertySource) { + properties.putAll(((PropertiesPropertySource) previous).asProperties(this.getScope())); + } else { + properties.putAll(previous.asProperties()); + } + } + return properties; + } + + @Override + public boolean containsKey(String key) { + return this.containsKey(this.getScope(), key); + } + + private boolean containsKey(ApplyScope scope, String key) { + for (AbstractPropertySource propertySource : PROPERTY_SOURCES) { + boolean containing; + if (propertySource instanceof PropertiesPropertySource) { + containing = ((PropertiesPropertySource) propertySource).containsKey(scope, key); + } else { + containing = propertySource.containsKey(key); + } + if (containing) { + return true; + } + } + return false; + } + + private Optional search(ApplyScope scope, String key, Class targetType) { + if (targetType == null) { + throw new IllegalArgumentException("target type must not be null!"); + } + + for (AbstractPropertySource propertySource : PROPERTY_SOURCES) { + String value; + if (propertySource instanceof PropertiesPropertySource) { + value = ((PropertiesPropertySource) propertySource).getProperty(scope, key); + } else { + value = propertySource.getProperty(key); + } + if (value != null) { + if (String.class.isAssignableFrom(targetType)) { + try { + return (Optional) Optional.of(value); + } catch (Exception e) { + LOGGER.error("target type convert error", e); + return Optional.empty(); + } + + } + return Optional.ofNullable(CONVERTER.convert(value, targetType)); + } + } + return Optional.empty(); + } + + protected ApplyScope getScope() { + return ApplyScope.GLOBAL; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java index 6aee164dad0..2514770c295 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java @@ -17,14 +17,11 @@ package com.alibaba.nacos.client.env; import java.util.Map; +import java.util.Properties; class SystemEnvPropertySource extends AbstractPropertySource { - private final Map env; - - SystemEnvPropertySource() { - this.env = System.getenv(); - } + private final Map env = System.getenv(); @Override SourceType getType() { @@ -78,7 +75,15 @@ private String checkPropertyName(String name) { return null; } - private boolean containsKey(String name) { + @Override + boolean containsKey(String name) { return this.env.containsKey(name); } + + @Override + Properties asProperties() { + Properties properties = new Properties(); + properties.putAll(this.env); + return properties; + } } diff --git a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java b/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java deleted file mode 100644 index 13bda0fab6e..00000000000 --- a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvironmentFactoryTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 1999-2022 Alibaba Group Holding Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.nacos.client.env; - -import org.junit.Assert; -import org.junit.Test; - -import java.lang.reflect.Proxy; -import java.util.Properties; - -public class NacosEnvironmentFactoryTest { - - @Test(expected = IllegalStateException.class) - public void testCreateEnvironment() { - final NacosEnvironment environment = NacosEnvironmentFactory.createEnvironment(); - Assert.assertNotNull(environment); - Assert.assertTrue(Proxy.isProxyClass(environment.getClass())); - environment.getProperty("test.exception"); - } - - @Test - public void testNacosEnvInit() { - final NacosEnvironment environment = NacosEnvironmentFactory.createEnvironment(); - final NacosEnvironmentFactory.NacosEnvironmentDelegate invocationHandler = - (NacosEnvironmentFactory.NacosEnvironmentDelegate) Proxy.getInvocationHandler( - environment); - Properties properties = new Properties(); - properties.setProperty("init.nacos", "true"); - - invocationHandler.init(properties); - - final String property = environment.getProperty("init.nacos"); - Assert.assertEquals("true", property); - } - -} diff --git a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java b/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java index 28ed1e6ddd2..fd4c80c565b 100644 --- a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java @@ -16,159 +16,174 @@ package com.alibaba.nacos.client.env; -import org.junit.AfterClass; import org.junit.Assert; -import org.junit.BeforeClass; import org.junit.Test; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.Properties; public class NacosEnvsTest { - static MockedStatic mockedStatic; - - @BeforeClass - public static void before() { - mockedStatic = Mockito.mockStatic(NacosEnvironmentFactory.class); - mockedStatic.when(NacosEnvironmentFactory::createEnvironment).thenReturn(createProxy()); - - } - - @AfterClass - public static void teardown() { - if (mockedStatic != null) { - mockedStatic.close(); - } - } - - private static NacosEnvironment createProxy() { - return (NacosEnvironment) Proxy.newProxyInstance(NacosEnvironmentFactory.class.getClassLoader(), - new Class[] {NacosEnvironment.class}, new NacosEnvironmentFactory.NacosEnvironmentDelegate() { - volatile NacosEnvironment environment; - - @Override - public void init(Properties properties) { - environment = new SearchableEnvironment(properties); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (environment == null) { - throw new IllegalStateException( - "Nacos environment doesn't init, please call NEnvs#init method then try it again."); - } - return method.invoke(environment, args); - } - }); - } - @Test public void testGetProperty() { - - final Properties properties = new Properties(); - properties.setProperty("nacos.home", "/home/nacos"); - NacosEnvs.init(properties); + NacosEnvs.setProperty("nacos.home", "/home/nacos"); final String value = NacosEnvs.getProperty("nacos.home"); - Assert.assertEquals("/home/nacos", value); } @Test public void testGetPropertyDefaultValue() { - - final Properties properties = new Properties(); - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home", "/home/default_value"); - + final String value = NacosEnvs.getProperty("nacos.home.default", "/home/default_value"); Assert.assertEquals("/home/default_value", value); } @Test public void testGetBoolean() { - final Properties properties = new Properties(); - properties.setProperty("use.cluster", "true"); - NacosEnvs.init(properties); - + NacosEnvs.setProperty("use.cluster", "true"); final Boolean value = NacosEnvs.getBoolean("use.cluster"); Assert.assertTrue(value); } @Test public void testGetBooleanDefaultValue() { - final Properties properties = new Properties(); - NacosEnvs.init(properties); - - final Boolean value = NacosEnvs.getBoolean("use.cluster", false); + final Boolean value = NacosEnvs.getBoolean("use.cluster.default", false); Assert.assertFalse(value); } @Test public void testGetInteger() { - final Properties properties = new Properties(); - properties.setProperty("max.timeout", "200"); - NacosEnvs.init(properties); - + NacosEnvs.setProperty("max.timeout", "200"); final Integer value = NacosEnvs.getInteger("max.timeout"); - Assert.assertEquals(200, value.intValue()); } @Test public void testGetIntegerDefaultValue() { - final Properties properties = new Properties(); - NacosEnvs.init(properties); - - final Integer value = NacosEnvs.getInteger("max.timeout", 400); + final Integer value = NacosEnvs.getInteger("max.timeout.default", 400); Assert.assertEquals(400, value.intValue()); } @Test public void testGetLong() { - final Properties properties = new Properties(); - properties.setProperty("connection.timeout", "200"); - NacosEnvs.init(properties); - + NacosEnvs.setProperty("connection.timeout", "200"); final Long value = NacosEnvs.getLong("connection.timeout"); Assert.assertEquals(200L, value.longValue()); } @Test public void testGetLongDefault() { - final Properties properties = new Properties(); - NacosEnvs.init(properties); - final Long value = NacosEnvs.getLong("connection.timeout", 400L); + final Long value = NacosEnvs.getLong("connection.timeout.default", 400L); Assert.assertEquals(400L, value.longValue()); } @Test - public void testGetPropertyJvmFirst() { - System.setProperty("nacos.envs.search", "jvm"); - System.setProperty("nacos.home", "/home/jvm_first"); - + public void testGetPropertyDefaultSetting() { + + final String value = NacosEnvs.getProperty("nacos.home.default.test"); + Assert.assertEquals("/home/default_setting", value); + } + + @Test + public void setProperty() { + NacosEnvs.setProperty("nacos.set.property", "true"); + final String ret = NacosEnvs.getProperty("nacos.set.property"); + Assert.assertEquals("true", ret); + } + + @Test + public void testAddProperties() { Properties properties = new Properties(); - properties.setProperty("nacos.home", "/home/properties_first"); + properties.setProperty("nacos.add.properties", "true"); - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home"); + NacosEnvs.addProperties(properties); + + final String ret = NacosEnvs.getProperty("nacos.add.properties"); - Assert.assertEquals("/home/jvm_first", value); - System.clearProperty("nacos.envs.search"); - System.clearProperty("nacos.home"); + Assert.assertEquals("true", ret); } @Test - public void testGetPropertyDefaultSetting() { - Properties properties = new Properties(); + public void testContainsKey() { + NacosEnvs.setProperty("nacos.contains.key", "true"); + + boolean ret = NacosEnvs.containsKey("nacos.contains.key"); + Assert.assertTrue(ret); + + ret = NacosEnvs.containsKey("nacos.contains.key.in.sys"); + Assert.assertFalse(ret); + } + + @Test + public void testContainsKeyWithScope() { + NacosEnvs.setProperty("nacos.contains.global.scope", "global"); + NacosEnvs.apply(ApplyScope.NAMING).setProperty("nacos.contains.naming.scope", "naming"); + + boolean ret = NacosEnvs.containsKey("nacos.contains.global.scope"); + Assert.assertTrue(ret); - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home.default.test"); + ret = NacosEnvs.containsKey("nacos.contains.naming.scope"); + Assert.assertFalse(ret); + + ret = NacosEnvs.apply(ApplyScope.NAMING).containsKey("nacos.contains.naming.scope"); + Assert.assertTrue(ret); - Assert.assertEquals("/home/default_setting", value); + ret = NacosEnvs.apply(ApplyScope.NAMING).containsKey("nacos.contains.global.scope"); + Assert.assertTrue(ret); + + } + + @Test + public void testAsProperties() { + NacosEnvs.setProperty("nacos.as.properties", "true"); + final Properties properties = NacosEnvs.asProperties(); + Assert.assertNotNull(properties); + Assert.assertEquals("true", properties.getProperty("nacos.as.properties")); + } + + @Test + public void testAsPropertiesWithScope() { + NacosEnvs.setProperty("nacos.as.properties.global.scope", "global"); + NacosEnvs.setProperty("nacos.server.addr.scope", "global"); + NacosEnvs.apply(ApplyScope.CONFIG).setProperty("nacos.server.addr.scope", "config"); + + final Properties properties = NacosEnvs.apply(ApplyScope.CONFIG).asProperties(); + Assert.assertNotNull(properties); + + String ret = properties.getProperty("nacos.as.properties.global.scope"); + Assert.assertEquals("global", ret); + + ret = properties.getProperty("nacos.server.addr.scope"); + Assert.assertEquals("config", ret); + } + + @Test + public void testGerPropertyWithScope() { + NacosEnvs.setProperty("nacos.global.scope", "global"); + NacosEnvs.apply(ApplyScope.CONFIG).setProperty("nacos.config.scope", "config"); + NacosEnvs.apply(ApplyScope.NAMING).setProperty("nacos.naming.scope", "naming"); + + String ret = NacosEnvs.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + + ret = NacosEnvs.getProperty("nacos.config.scope"); + Assert.assertNull(ret); + + ret = NacosEnvs.getProperty("nacos.naming.scope"); + Assert.assertNull(ret); + + ret = NacosEnvs.apply(ApplyScope.CONFIG).getProperty("nacos.config.scope"); + Assert.assertEquals("config", ret); + ret = NacosEnvs.apply(ApplyScope.CONFIG).getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + ret = NacosEnvs.apply(ApplyScope.CONFIG).getProperty("nacos.naming.scope"); + Assert.assertNull(ret); + + ret = NacosEnvs.apply(ApplyScope.NAMING).getProperty("nacos.naming.scope"); + Assert.assertEquals("naming", ret); + ret = NacosEnvs.apply(ApplyScope.NAMING).getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + ret = NacosEnvs.apply(ApplyScope.NAMING).getProperty("nacos.config.scope"); + Assert.assertNull(ret); } }