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 53a35d574..f38cc7db7 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 class Constants { 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 3b34951d6..779580a18 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/DefaultSettingPropertySource.java b/client/src/main/java/com/alibaba/nacos/client/env/DefaultSettingPropertySource.java index 6539c6989..6990e0e47 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.Logger; 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 @@ class DefaultSettingPropertySource extends AbstractPropertySource { 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 5c4475c05..e9448d8df 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 @@ -35,4 +35,16 @@ class JvmArgsPropertySource extends AbstractPropertySource { 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/NacosClientProperties.java similarity index 58% rename from client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java rename to client/src/main/java/com/alibaba/nacos/client/env/NacosClientProperties.java index 14269fc6d..021ae97d1 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvironment.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/NacosClientProperties.java @@ -16,12 +16,27 @@ package com.alibaba.nacos.client.env; +import java.util.Properties; + /** - * nacos env interface. - * + * NacosClientProperties interface. + * include all the properties from jvm args, system environment, default setting. + * more details you can see https://github.com/alibaba/nacos/issues/8622 * @author onewe */ -public interface NacosEnvironment { +public interface NacosClientProperties { + + /** + * all the NacosClientProperties object must be created by PROTOTYPE, + * so child NacosClientProperties can read properties from the PROTOTYPE. + * it looks like this: + * |-PROTOTYPE----------------> ip=127.0.0.1 + * |---|-child1---------------> port=6379 + * if you search key called "port" from child1, certainly you will get 6379 + * if you search key called "ip" from child1, you will get 127.0.0.1. + * because the child can read properties from parent NacosClientProperties + */ + NacosClientProperties PROTOTYPE = SearchableProperties.INSTANCE; /** * get property, if the value can not be got by the special key, the null will be returned. @@ -87,4 +102,42 @@ 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 NacosClientProperties. + * @param key key – possible key + * @return true if and only if the specified object is a key in this NacosClientProperties, false otherwise. + */ + boolean containsKey(String key); + + /** + * get properties from NacosClientProperties. + * @return properties + */ + Properties asProperties(); + + /** + * create a new NacosClientProperties which scope is itself. + * @return NacosClientProperties + */ + NacosClientProperties derive(); + + /** + * create a new NacosClientProperties from NacosClientProperties#PROTOTYPE and init. + * @param properties properties + * @return NacosClientProperties + */ + NacosClientProperties derive(Properties properties); } 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 74e65a461..000000000 --- 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 deleted file mode 100644 index 8510e575a..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/NacosEnvs.java +++ /dev/null @@ -1,116 +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.Proxy; -import java.util.Properties; - -/** - * environment utils. - * @author onewe - */ -public class NacosEnvs { - - private static final NacosEnvironment ENVIRONMENT = NacosEnvironmentFactory.createEnvironment(); - - /** - * init environment. - * @param properties properties - */ - public static void init(Properties properties) { - NacosEnvironmentFactory.NacosEnvironmentDelegate warrper = (NacosEnvironmentFactory.NacosEnvironmentDelegate) Proxy.getInvocationHandler( - ENVIRONMENT); - warrper.init(properties); - } - - public static String getProperty(String key, String defaultValue) { - return ENVIRONMENT.getProperty(key, defaultValue); - } - - /** - * get property, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return string value or null. - */ - public static String getProperty(String key) { - return ENVIRONMENT.getProperty(key); - } - - /** - * get boolean, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return boolean value or null. - */ - public static Boolean getBoolean(String key) { - return ENVIRONMENT.getBoolean(key); - } - - /** - * get boolean, if the value can not be got by the special key, the default value will be returned. - * - * @param key special key - * @param defaultValue default value - * @return boolean value or defaultValue. - */ - public static Boolean getBoolean(String key, Boolean defaultValue) { - return ENVIRONMENT.getBoolean(key, defaultValue); - } - - /** - * get integer, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return integer value or null - */ - public static Integer getInteger(String key) { - return ENVIRONMENT.getInteger(key); - } - - /** - * get integer, if the value can not be got by the special key, the default value will be returned. - * - * @param key special key - * @param defaultValue default value - * @return integer value or default value - */ - public static Integer getInteger(String key, Integer defaultValue) { - return ENVIRONMENT.getInteger(key, defaultValue); - } - - /** - * get long, if the value can not be got by the special key, the null will be returned. - * - * @param key special key - * @return long value or null - */ - public static Long getLong(String key) { - return ENVIRONMENT.getLong(key); - } - - /** - * get long, if the value can not be got by the special key, the default value will be returned. - * - * @param key special key - * @param defaultValue default value - * @return long value or default value - */ - public static Long getLong(String key, Long defaultValue) { - return ENVIRONMENT.getLong(key, defaultValue); - } -} 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 991f83a4d..6ae9d1fa6 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,23 @@ package com.alibaba.nacos.client.env; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; import java.util.Properties; class PropertiesPropertySource extends AbstractPropertySource { - private final Properties properties; + private final Properties properties = new Properties(); - PropertiesPropertySource(Properties properties) { - this.properties = properties; + private final PropertiesPropertySource parent; + + PropertiesPropertySource() { + this.parent = null; + } + + PropertiesPropertySource(PropertiesPropertySource parent) { + this.parent = parent; } @Override @@ -33,6 +42,67 @@ class PropertiesPropertySource extends AbstractPropertySource { @Override String getProperty(String key) { - return properties.getProperty(key); + return getProperty(this, key); + } + + private String getProperty(PropertiesPropertySource propertiesPropertySource, String key) { + final String value = propertiesPropertySource.properties.getProperty(key); + if (value != null) { + return value; + } + final PropertiesPropertySource parent = propertiesPropertySource.parent; + if (parent == null) { + return null; + } + return getProperty(parent, key); + } + + @Override + boolean containsKey(String key) { + return containsKey(this, key); + } + + boolean containsKey(PropertiesPropertySource propertiesPropertySource, String key) { + final boolean exist = propertiesPropertySource.properties.containsKey(key); + if (exist) { + return true; + } + final PropertiesPropertySource parent = propertiesPropertySource.parent; + if (parent == null) { + return false; + } + return containsKey(parent, key); + } + + @Override + Properties asProperties() { + List propertiesList = new ArrayList<>(8); + + propertiesList = lookForProperties(this, propertiesList); + + Properties ret = new Properties(); + final ListIterator iterator = propertiesList.listIterator(propertiesList.size()); + while (iterator.hasPrevious()) { + final Properties properties = iterator.previous(); + ret.putAll(properties); + } + return ret; + } + + List lookForProperties(PropertiesPropertySource propertiesPropertySource, List propertiesList) { + propertiesList.add(propertiesPropertySource.properties); + final PropertiesPropertySource parent = propertiesPropertySource.parent; + if (parent == null) { + return propertiesList; + } + return lookForProperties(parent, propertiesList); + } + + synchronized void setProperty(String key, String value) { + properties.setProperty(key, value); + } + + synchronized void addProperties(Properties source) { + properties.putAll(source); } } 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 e73dc995a..000000000 --- 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 deleted file mode 100644 index 7dbb6bc5c..000000000 --- a/client/src/main/java/com/alibaba/nacos/client/env/SearchableEnvironment.java +++ /dev/null @@ -1,74 +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.util.Properties; - -/** - * Searchable environment. - * - * @author onewe - */ -class SearchableEnvironment implements NacosEnvironment { - - private final PropertySourceSearch sourceSearch; - - SearchableEnvironment(Properties properties) { - this.sourceSearch = PropertySourceSearch.build(properties); - } - - @Override - public String getProperty(String key) { - return getProperty(key, null); - } - - @Override - public String getProperty(String key, String defaultValue) { - return sourceSearch.search(key, String.class).orElse(defaultValue); - } - - @Override - public Boolean getBoolean(String key) { - return getBoolean(key, null); - } - - @Override - public Boolean getBoolean(String key, Boolean defaultValue) { - return sourceSearch.search(key, Boolean.class).orElse(defaultValue); - } - - @Override - public Integer getInteger(String key) { - return getInteger(key, null); - } - - @Override - public Integer getInteger(String key, Integer defaultValue) { - return sourceSearch.search(key, Integer.class).orElse(defaultValue); - } - - @Override - public Long getLong(String key) { - return getLong(key, null); - } - - @Override - public Long getLong(String key, Long defaultValue) { - return sourceSearch.search(key, Long.class).orElse(defaultValue); - } - -} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java b/client/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java new file mode 100644 index 000000000..ac7d5b92f --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java @@ -0,0 +1,236 @@ +/* + * 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.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * Searchable NacosClientProperties. + * the SearchableProperties that it can be specified search order by + * nacos.env.first + * @author onewe + */ +class SearchableProperties implements NacosClientProperties { + + private static final Logger LOGGER = LoggerFactory.getLogger(SearchableProperties.class); + + private static final JvmArgsPropertySource JVM_ARGS_PROPERTY_SOURCE = new JvmArgsPropertySource(); + + private static final SystemEnvPropertySource SYSTEM_ENV_PROPERTY_SOURCE = new SystemEnvPropertySource(); + + private static final DefaultSettingPropertySource DEFAULT_SETTING_PROPERTY_SOURCE = new DefaultSettingPropertySource(); + + private static final List DEFAULT_ORDER = Arrays.asList(SourceType.PROPERTIES, SourceType.JVM, + SourceType.ENV, SourceType.DEFAULT_SETTING); + + private static final CompositeConverter CONVERTER = new CompositeConverter(); + + static final SearchableProperties INSTANCE = new SearchableProperties(); + + private final List propertySources; + + private final PropertiesPropertySource propertiesPropertySource; + + private SearchableProperties() { + this(new PropertiesPropertySource()); + } + + private SearchableProperties(PropertiesPropertySource propertiesPropertySource) { + this.propertiesPropertySource = propertiesPropertySource; + this.propertySources = build(propertiesPropertySource, + JVM_ARGS_PROPERTY_SOURCE, SYSTEM_ENV_PROPERTY_SOURCE, DEFAULT_SETTING_PROPERTY_SOURCE); + } + + @Override + public String getProperty(String key) { + return getProperty(key, null); + } + + @Override + public String getProperty(String key, String defaultValue) { + return this.search(key, String.class).orElse(defaultValue); + } + + @Override + public Boolean getBoolean(String key) { + return getBoolean(key, null); + } + + @Override + public Boolean getBoolean(String key, Boolean defaultValue) { + return this.search(key, Boolean.class).orElse(defaultValue); + } + + @Override + public Integer getInteger(String key) { + return getInteger(key, null); + } + + @Override + public Integer getInteger(String key, Integer defaultValue) { + return this.search(key, Integer.class).orElse(defaultValue); + } + + @Override + public Long getLong(String key) { + return getLong(key, null); + } + + @Override + public Long getLong(String key, Long defaultValue) { + return this.search(key, Long.class).orElse(defaultValue); + } + + @Override + public void setProperty(String key, String value) { + propertiesPropertySource.setProperty(key, value); + } + + @Override + public void addProperties(Properties properties) { + propertiesPropertySource.addProperties(properties); + } + + @Override + public Properties asProperties() { + Properties properties = new Properties(); + final ListIterator iterator = propertySources.listIterator( + propertySources.size()); + while (iterator.hasPrevious()) { + final AbstractPropertySource previous = iterator.previous(); + properties.putAll(previous.asProperties()); + } + return properties; + } + + @Override + public boolean containsKey(String key) { + for (AbstractPropertySource propertySource : propertySources) { + final boolean containing = propertySource.containsKey(key); + if (containing) { + return true; + } + } + return false; + } + + private Optional search(String key, Class targetType) { + if (targetType == null) { + throw new IllegalArgumentException("target type must not be null!"); + } + + for (AbstractPropertySource propertySource : propertySources) { + final String 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(); + } + + private List build(AbstractPropertySource... propertySources) { + + String firstEnv = JVM_ARGS_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); + if (StringUtils.isBlank(firstEnv)) { + firstEnv = SYSTEM_ENV_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); + } + + if (StringUtils.isBlank(firstEnv)) { + return sortPropertySourceDefaultOrder(propertySources); + } + + try { + final SourceType sourceType = SourceType.valueOf(firstEnv.toUpperCase()); + if (SourceType.DEFAULT_SETTING.equals(sourceType) || SourceType.PROPERTIES.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!", e); + return sortPropertySourceDefaultOrder(propertySources); + } + } + + private 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("properties search order:PROPERTIES->JVM->ENV->DEFAULT_SETTING"); + return collect; + } + + private 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("properties search order:"); + for (int i = 0; i < collect.size(); i++) { + final AbstractPropertySource abstractPropertySource = collect.get(i); + orderInfo.append(abstractPropertySource.getType().toString()); + if (i < collect.size() - 1) { + orderInfo.append("->"); + } + } + LOGGER.info(orderInfo.toString()); + + return collect; + } + + @Override + public NacosClientProperties derive() { + return new SearchableProperties(new PropertiesPropertySource(this.propertiesPropertySource)); + } + + @Override + public NacosClientProperties derive(Properties properties) { + final NacosClientProperties nacosClientProperties = this.derive(); + nacosClientProperties.addProperties(properties); + return nacosClientProperties; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java b/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java index 8a5d1096b..4727199c6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java +++ b/client/src/main/java/com/alibaba/nacos/client/env/SourceType.java @@ -18,17 +18,17 @@ package com.alibaba.nacos.client.env; enum SourceType { /** - * get value from system environment. + * get value from properties. */ - SYS, + PROPERTIES, /** * get value from jvm args. */ JVM, /** - * get value from properties. + * get value from system environment. */ - PROPERTIES, + ENV, /** * get value from default setting. */ 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 6aee164da..d8689f130 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,18 +17,15 @@ 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() { - return SourceType.SYS; + return SourceType.ENV; } @Override @@ -78,7 +75,15 @@ class SystemEnvPropertySource extends AbstractPropertySource { 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/NacosClientPropertiesTest.java b/client/src/test/java/com/alibaba/nacos/client/env/NacosClientPropertiesTest.java new file mode 100644 index 000000000..6e0c361e1 --- /dev/null +++ b/client/src/test/java/com/alibaba/nacos/client/env/NacosClientPropertiesTest.java @@ -0,0 +1,308 @@ +/* + * 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.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Properties; + +public class NacosClientPropertiesTest { + + @BeforeClass + public static void init() { + System.setProperty("nacos.env.first", "jvm"); + } + + @AfterClass + public static void teardown() { + System.clearProperty("nacos.env.first"); + } + + @Test + public void testGetProperty() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.home", "/home/nacos"); + final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home"); + Assert.assertEquals("/home/nacos", value); + } + + @Test + public void testGetPropertyMultiLayer() { + + NacosClientProperties.PROTOTYPE.setProperty("top.layer", "top"); + + final NacosClientProperties layerAEnv = NacosClientProperties.PROTOTYPE.derive(); + layerAEnv.setProperty("a.layer", "a"); + + final NacosClientProperties layerBEnv = layerAEnv.derive(); + layerBEnv.setProperty("b.layer", "b"); + + final NacosClientProperties layerCEnv = layerBEnv.derive(); + layerCEnv.setProperty("c.layer", "c"); + + String value = layerCEnv.getProperty("c.layer"); + Assert.assertEquals("c", value); + + value = layerCEnv.getProperty("b.layer"); + Assert.assertEquals("b", value); + + value = layerCEnv.getProperty("a.layer"); + Assert.assertEquals("a", value); + + value = layerCEnv.getProperty("top.layer"); + Assert.assertEquals("top", value); + } + + @Test + public void testGetPropertyDefaultValue() { + final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home.default", "/home/default_value"); + Assert.assertEquals("/home/default_value", value); + } + + @Test + public void testGetBoolean() { + NacosClientProperties.PROTOTYPE.setProperty("use.cluster", "true"); + final Boolean value = NacosClientProperties.PROTOTYPE.getBoolean("use.cluster"); + Assert.assertTrue(value); + } + + @Test + public void testGetBooleanDefaultValue() { + final Boolean value = NacosClientProperties.PROTOTYPE.getBoolean("use.cluster.default", false); + Assert.assertFalse(value); + } + + @Test + public void testGetInteger() { + NacosClientProperties.PROTOTYPE.setProperty("max.timeout", "200"); + final Integer value = NacosClientProperties.PROTOTYPE.getInteger("max.timeout"); + Assert.assertEquals(200, value.intValue()); + } + + @Test + public void testGetIntegerDefaultValue() { + final Integer value = NacosClientProperties.PROTOTYPE.getInteger("max.timeout.default", 400); + Assert.assertEquals(400, value.intValue()); + } + + @Test + public void testGetLong() { + NacosClientProperties.PROTOTYPE.setProperty("connection.timeout", "200"); + final Long value = NacosClientProperties.PROTOTYPE.getLong("connection.timeout"); + Assert.assertEquals(200L, value.longValue()); + } + + @Test + public void testGetLongDefault() { + final Long value = NacosClientProperties.PROTOTYPE.getLong("connection.timeout.default", 400L); + Assert.assertEquals(400L, value.longValue()); + } + + @Test + public void testGetPropertyDefaultSetting() { + + final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home.default.test"); + Assert.assertEquals("/home/default_setting", value); + } + + @Test + public void setProperty() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.set.property", "true"); + final String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.set.property"); + Assert.assertEquals("true", ret); + } + + @Test + public void setPropertyWithScope() { + + final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); + properties.setProperty("nacos.set.property.scope", "config"); + + String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.set.property.scope"); + Assert.assertNull(ret); + + ret = properties.getProperty("nacos.set.property.scope"); + Assert.assertEquals("config", ret); + } + + @Test + public void testAddProperties() { + Properties properties = new Properties(); + properties.setProperty("nacos.add.properties", "true"); + + NacosClientProperties.PROTOTYPE.addProperties(properties); + + final String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.add.properties"); + + Assert.assertEquals("true", ret); + } + + @Test + public void testAddPropertiesWithScope() { + + Properties properties = new Properties(); + properties.setProperty("nacos.add.properties.scope", "config"); + + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(); + nacosClientProperties.addProperties(properties); + + String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.add.properties.scope"); + Assert.assertNull(ret); + + ret = nacosClientProperties.getProperty("nacos.add.properties.scope"); + Assert.assertEquals("config", ret); + + } + + @Test + public void testTestDerive() { + Properties properties = new Properties(); + properties.setProperty("nacos.derive.properties.scope", "derive"); + + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); + + final String value = nacosClientProperties.getProperty("nacos.derive.properties.scope"); + + Assert.assertEquals("derive", value); + + } + + @Test + public void testContainsKey() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.contains.key", "true"); + + boolean ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.key"); + Assert.assertTrue(ret); + + ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.key.in.sys"); + Assert.assertFalse(ret); + } + + @Test + public void testContainsKeyMultiLayers() { + + NacosClientProperties.PROTOTYPE.setProperty("top.layer", "top"); + + final NacosClientProperties layerAEnv = NacosClientProperties.PROTOTYPE.derive(); + layerAEnv.setProperty("a.layer", "a"); + + final NacosClientProperties layerBEnv = layerAEnv.derive(); + layerBEnv.setProperty("b.layer", "b"); + + final NacosClientProperties layerCEnv = layerBEnv.derive(); + layerCEnv.setProperty("c.layer", "c"); + + boolean exist = layerCEnv.containsKey("c.layer"); + Assert.assertTrue(exist); + + exist = layerCEnv.containsKey("b.layer"); + Assert.assertTrue(exist); + + exist = layerCEnv.containsKey("a.layer"); + Assert.assertTrue(exist); + + exist = layerCEnv.containsKey("top.layer"); + Assert.assertTrue(exist); + + } + + @Test + public void testContainsKeyWithScope() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.contains.global.scope", "global"); + final NacosClientProperties namingProperties = NacosClientProperties.PROTOTYPE.derive(); + namingProperties.setProperty("nacos.contains.naming.scope", "naming"); + + boolean ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.global.scope"); + Assert.assertTrue(ret); + + ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.naming.scope"); + Assert.assertFalse(ret); + + ret = namingProperties.containsKey("nacos.contains.naming.scope"); + Assert.assertTrue(ret); + + ret = namingProperties.containsKey("nacos.contains.global.scope"); + Assert.assertTrue(ret); + + } + + @Test + public void testAsProperties() { + NacosClientProperties.PROTOTYPE.setProperty("nacos.as.properties", "true"); + final Properties properties = NacosClientProperties.PROTOTYPE.asProperties(); + Assert.assertNotNull(properties); + Assert.assertEquals("true", properties.getProperty("nacos.as.properties")); + } + + @Test + public void testAsPropertiesWithScope() { + + NacosClientProperties.PROTOTYPE.setProperty("nacos.as.properties.global.scope", "global"); + NacosClientProperties.PROTOTYPE.setProperty("nacos.server.addr.scope", "global"); + + final NacosClientProperties configProperties = NacosClientProperties.PROTOTYPE.derive(); + configProperties.setProperty("nacos.server.addr.scope", "config"); + + final Properties properties = configProperties.asProperties(); + 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 testGetPropertyWithScope() { + + NacosClientProperties.PROTOTYPE.setProperty("nacos.global.scope", "global"); + + final NacosClientProperties configProperties = NacosClientProperties.PROTOTYPE.derive(); + configProperties.setProperty("nacos.config.scope", "config"); + + final NacosClientProperties namingProperties = NacosClientProperties.PROTOTYPE.derive(); + namingProperties.setProperty("nacos.naming.scope", "naming"); + + String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + + ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.config.scope"); + Assert.assertNull(ret); + + ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.naming.scope"); + Assert.assertNull(ret); + + ret = configProperties.getProperty("nacos.config.scope"); + Assert.assertEquals("config", ret); + ret = configProperties.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + ret = configProperties.getProperty("nacos.naming.scope"); + Assert.assertNull(ret); + + ret = namingProperties.getProperty("nacos.naming.scope"); + Assert.assertEquals("naming", ret); + ret = namingProperties.getProperty("nacos.global.scope"); + Assert.assertEquals("global", ret); + ret = namingProperties.getProperty("nacos.config.scope"); + Assert.assertNull(ret); + + } + +} 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 13bda0fab..000000000 --- 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 deleted file mode 100644 index 28ed1e6dd..000000000 --- a/client/src/test/java/com/alibaba/nacos/client/env/NacosEnvsTest.java +++ /dev/null @@ -1,174 +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.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); - 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"); - - Assert.assertEquals("/home/default_value", value); - } - - @Test - public void testGetBoolean() { - final Properties properties = new Properties(); - properties.setProperty("use.cluster", "true"); - NacosEnvs.init(properties); - - 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); - Assert.assertFalse(value); - } - - @Test - public void testGetInteger() { - final Properties properties = new Properties(); - properties.setProperty("max.timeout", "200"); - NacosEnvs.init(properties); - - 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); - Assert.assertEquals(400, value.intValue()); - } - - @Test - public void testGetLong() { - final Properties properties = new Properties(); - properties.setProperty("connection.timeout", "200"); - NacosEnvs.init(properties); - - 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); - Assert.assertEquals(400L, value.longValue()); - } - - @Test - public void testGetPropertyJvmFirst() { - System.setProperty("nacos.envs.search", "jvm"); - System.setProperty("nacos.home", "/home/jvm_first"); - - Properties properties = new Properties(); - properties.setProperty("nacos.home", "/home/properties_first"); - - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home"); - - Assert.assertEquals("/home/jvm_first", value); - System.clearProperty("nacos.envs.search"); - System.clearProperty("nacos.home"); - } - - @Test - public void testGetPropertyDefaultSetting() { - Properties properties = new Properties(); - - NacosEnvs.init(properties); - final String value = NacosEnvs.getProperty("nacos.home.default.test"); - - Assert.assertEquals("/home/default_setting", value); - - } - -}