From 3f672a82130fdda33115553514fa792a053a9c09 Mon Sep 17 00:00:00 2001 From: "lingxiao,wu" <51630311+lingxiao-wu@users.noreply.github.com> Date: Thu, 11 Aug 2022 19:37:21 +0800 Subject: [PATCH] feature:add @ConditionalOnConfigReflectEnabled annotation (#496) * feature:#470 * feature:add @ConditionalOnConfigReflectEnabled annotation * feature:add @ConditionalOnConfigReflectEnabled annotation * feature:add @ConditionalOnConfigReflectEnabled annotation * feature:add @ConditionalOnConfigReflectEnabled annotation * feature:add @ConditionalOnConfigReflectEnabled annotation * feature:add @ConditionalOnConfigReflectEnabled annotation * feature:add @ConditionalOnConfigReflectEnabled annotation Co-authored-by: wulingxiao <1251605638@qqcom> Co-authored-by: VOPEN.XYZ --- CHANGELOG.md | 3 +- .../PolarisConfigAutoConfiguration.java | 66 +++--- .../PolarisConfigPropertyAutoRefresher.java | 115 +++++++++ .../PolarisConfigPropertyRefresher.java | 44 ++++ .../PolarisPropertySourceAutoRefresher.java | 219 ------------------ ...risReflectConfigPropertyAutoRefresher.java | 137 +++++++++++ ...eshContextConfigPropertyAutoRefresher.java | 52 +++++ .../ConditionalOnConfigReflectEnabled.java | 37 +++ .../condition/ConfigReflectCondition.java | 55 +++++ ...arisPropertiesSourceAutoRefresherTest.java | 12 +- ...ConditionalOnConfigReflectEnabledTest.java | 83 +++++++ .../metadata/StaticMetadataManagerTest.java | 2 +- 12 files changed, 568 insertions(+), 257 deletions(-) create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java delete mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisReflectConfigPropertyAutoRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshContextConfigPropertyAutoRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabled.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConfigReflectCondition.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabledTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index cc24833c..4e75f71b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,4 +6,5 @@ - [Feature: delete implement ServiceInstance](https://github.com/Tencent/spring-cloud-tencent/pull/481) - [Upgrade owasp esapi's configuration](https://github.com/Tencent/spring-cloud-tencent/pull/492) - [Bugfix: update byte-buddy scope test to compile](https://github.com/Tencent/spring-cloud-tencent/pull/495) -- [Feature: zuul supports polaris router](https://github.com/Tencent/spring-cloud-tencent/pull/502) \ No newline at end of file +- [feature:add @ConditionalOnConfigReflectEnabled annotation](https://github.com/Tencent/spring-cloud-tencent/pull/496) +- [Feature: zuul supports polaris router](https://github.com/Tencent/spring-cloud-tencent/pull/502) diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java index 980b985d..60201c72 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java @@ -18,10 +18,13 @@ package com.tencent.cloud.polaris.config; -import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceAutoRefresher; +import com.tencent.cloud.polaris.config.adapter.PolarisConfigPropertyRefresher; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.adapter.PolarisReflectConfigPropertyAutoRefresher; +import com.tencent.cloud.polaris.config.adapter.PolarisRefreshContextConfigPropertyAutoRefresher; import com.tencent.cloud.polaris.config.adapter.SmartConfigurationPropertiesRebinder; import com.tencent.cloud.polaris.config.annotation.PolarisConfigAnnotationProcessor; +import com.tencent.cloud.polaris.config.condition.ConditionalOnConfigReflectEnabled; import com.tencent.cloud.polaris.config.condition.ConditionalOnNonDefaultBehavior; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.listener.PolarisConfigChangeEventListener; @@ -30,6 +33,7 @@ import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SearchStrategy; @@ -49,17 +53,6 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", matchIfMissing = true) public class PolarisConfigAutoConfiguration { - @Bean - public PolarisPropertySourceAutoRefresher polarisPropertySourceAutoRefresher( - PolarisConfigProperties polarisConfigProperties, - PolarisPropertySourceManager polarisPropertySourceManager, - SpringValueRegistry springValueRegistry, - PlaceholderHelper placeholderHelper, - ContextRefresher contextRefresher) { - return new PolarisPropertySourceAutoRefresher(polarisConfigProperties, - polarisPropertySourceManager, springValueRegistry, placeholderHelper, contextRefresher); - } - @Bean public PolarisConfigAnnotationProcessor polarisConfigAnnotationProcessor() { return new PolarisConfigAnnotationProcessor(); @@ -70,21 +63,6 @@ public class PolarisConfigAutoConfiguration { return new PolarisConfigChangeEventListener(); } - @Bean - public SpringValueRegistry springValueRegistry() { - return new SpringValueRegistry(); - } - - @Bean - public PlaceholderHelper placeholderHelper() { - return new PlaceholderHelper(); - } - - @Bean - public SpringValueProcessor springValueProcessor(PlaceholderHelper placeholderHelper, SpringValueRegistry springValueRegistry, PolarisConfigProperties polarisConfigProperties) { - return new SpringValueProcessor(placeholderHelper, springValueRegistry, polarisConfigProperties); - } - @Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) @ConditionalOnNonDefaultBehavior @@ -95,4 +73,38 @@ public class PolarisConfigAutoConfiguration { return new SmartConfigurationPropertiesRebinder(beans); } + @Bean + @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) + public PolarisConfigPropertyRefresher polarisRefreshContextPropertySourceAutoRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, ContextRefresher contextRefresher) { + return new PolarisRefreshContextConfigPropertyAutoRefresher(polarisConfigProperties, polarisPropertySourceManager, contextRefresher); + } + + @ConditionalOnConfigReflectEnabled + @Configuration(proxyBeanMethods = false) + @AutoConfigureBefore(PolarisConfigAutoConfiguration.class) + public static class PolarisReflectRefresherAutoConfiguration { + @Bean + public SpringValueRegistry springValueRegistry() { + return new SpringValueRegistry(); + } + + @Bean + public PlaceholderHelper placeholderHelper() { + return new PlaceholderHelper(); + } + + @Bean + public SpringValueProcessor springValueProcessor(PlaceholderHelper placeholderHelper, + SpringValueRegistry springValueRegistry, PolarisConfigProperties polarisConfigProperties) { + return new SpringValueProcessor(placeholderHelper, springValueRegistry, polarisConfigProperties); + } + + @Bean + public PolarisConfigPropertyRefresher polarisReflectPropertySourceAutoRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, SpringValueRegistry springValueRegistry, + PlaceholderHelper placeholderHelper) { + return new PolarisReflectConfigPropertyAutoRefresher(polarisConfigProperties, polarisPropertySourceManager, springValueRegistry, placeholderHelper); + } + } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java new file mode 100644 index 00000000..275258c8 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.util.CollectionUtils; + +/** + * 1. Listen to the Polaris server configuration publishing event 2. Write the changed + * configuration content to propertySource 3. Refresh the context through contextRefresher + * + * @author lepdou 2022-03-28 + */ +public abstract class PolarisConfigPropertyAutoRefresher + implements ApplicationListener, PolarisConfigPropertyRefresher { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigPropertyAutoRefresher.class); + + private final PolarisConfigProperties polarisConfigProperties; + + private final PolarisPropertySourceManager polarisPropertySourceManager; + + private final AtomicBoolean registered = new AtomicBoolean(false); + + public PolarisConfigPropertyAutoRefresher( + PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager) { + this.polarisConfigProperties = polarisConfigProperties; + this.polarisPropertySourceManager = polarisPropertySourceManager; + } + + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + registerPolarisConfigPublishEvent(); + } + + private void registerPolarisConfigPublishEvent() { + if (!polarisConfigProperties.isAutoRefresh()) { + return; + } + + List polarisPropertySources = polarisPropertySourceManager.getAllPropertySources(); + if (CollectionUtils.isEmpty(polarisPropertySources)) { + return; + } + + if (!registered.compareAndSet(false, true)) { + return; + } + + // register polaris config publish event + for (PolarisPropertySource polarisPropertySource : polarisPropertySources) { + polarisPropertySource.getConfigKVFile() + .addChangeListener((ConfigKVFileChangeListener) configKVFileChangeEvent -> { + + LOGGER.info( + "[SCT Config] received polaris config change event and will refresh spring context." + + " namespace = {}, group = {}, fileName = {}", + polarisPropertySource.getNamespace(), + polarisPropertySource.getGroup(), + polarisPropertySource.getFileName()); + + Map source = polarisPropertySource.getSource(); + + for (String changedKey : configKVFileChangeEvent.changedKeys()) { + ConfigPropertyChangeInfo configPropertyChangeInfo = configKVFileChangeEvent + .getChangeInfo(changedKey); + + LOGGER.info("[SCT Config] changed property = {}", configPropertyChangeInfo); + + switch (configPropertyChangeInfo.getChangeType()) { + case MODIFIED: + case ADDED: + source.put(changedKey, configPropertyChangeInfo.getNewValue()); + break; + case DELETED: + source.remove(changedKey); + break; + } + // update the attribute with @Value annotation + refreshSpringValue(changedKey); + } + // update @ConfigurationProperties beans + refreshConfigurationProperties(configKVFileChangeEvent.changedKeys()); + }); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java new file mode 100644 index 00000000..39351e7b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.util.Set; + +/** + * PolarisConfigPropertyRefresher refresh spring value filed and configurationProperties bean + * when config exchange. + * + * @author lingxiao.wlx + */ +public interface PolarisConfigPropertyRefresher { + + /** + * refresh the attribute with @Value annotation. + * + * @param changedKey changedKey + */ + void refreshSpringValue(String changedKey); + + /** + * refresh @ConfigurationProperties beans. + * + * @param changeKeys changeKeys + */ + void refreshConfigurationProperties(Set changeKeys); +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java deleted file mode 100644 index 5e5e6a3f..00000000 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * 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.tencent.cloud.polaris.config.adapter; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.tencent.cloud.common.util.JacksonUtils; -import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; -import com.tencent.cloud.polaris.config.enums.RefreshType; -import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; -import com.tencent.cloud.polaris.config.spring.property.SpringValue; -import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; -import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; -import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.BeansException; -import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.cloud.context.environment.EnvironmentChangeEvent; -import org.springframework.cloud.context.refresh.ContextRefresher; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.util.CollectionUtils; - -/** - * 1. Listen to the Polaris server configuration publishing event 2. Write the changed - * configuration content to propertySource 3. Refresh the context through contextRefresher - * - * @author lepdou 2022-03-28 - */ -public class PolarisPropertySourceAutoRefresher - implements ApplicationListener, ApplicationContextAware, BeanFactoryAware { - - private static final Logger LOGGER = LoggerFactory.getLogger(PolarisPropertySourceAutoRefresher.class); - - private final PolarisConfigProperties polarisConfigProperties; - - private final PolarisPropertySourceManager polarisPropertySourceManager; - - private final ContextRefresher contextRefresher; - - private final AtomicBoolean registered = new AtomicBoolean(false); - - private ConfigurableApplicationContext context; - - private TypeConverter typeConverter; - private final SpringValueRegistry springValueRegistry; - private ConfigurableBeanFactory beanFactory; - private final PlaceholderHelper placeholderHelper; - - public PolarisPropertySourceAutoRefresher( - PolarisConfigProperties polarisConfigProperties, - PolarisPropertySourceManager polarisPropertySourceManager, - SpringValueRegistry springValueRegistry, - PlaceholderHelper placeholderHelper, - ContextRefresher contextRefresher) { - this.polarisConfigProperties = polarisConfigProperties; - this.polarisPropertySourceManager = polarisPropertySourceManager; - this.springValueRegistry = springValueRegistry; - this.placeholderHelper = placeholderHelper; - this.contextRefresher = contextRefresher; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.context = (ConfigurableApplicationContext) applicationContext; - this.beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); - this.typeConverter = this.beanFactory.getTypeConverter(); - } - - @Override - public void onApplicationEvent(ApplicationReadyEvent event) { - registerPolarisConfigPublishEvent(); - } - - private void registerPolarisConfigPublishEvent() { - if (!polarisConfigProperties.isAutoRefresh()) { - return; - } - - List polarisPropertySources = polarisPropertySourceManager.getAllPropertySources(); - if (CollectionUtils.isEmpty(polarisPropertySources)) { - return; - } - - if (!registered.compareAndSet(false, true)) { - return; - } - - // register polaris config publish event - for (PolarisPropertySource polarisPropertySource : polarisPropertySources) { - polarisPropertySource.getConfigKVFile() - .addChangeListener((ConfigKVFileChangeListener) configKVFileChangeEvent -> { - - LOGGER.info( - "[SCT Config] received polaris config change event and will refresh spring context." - + " namespace = {}, group = {}, fileName = {}", - polarisPropertySource.getNamespace(), - polarisPropertySource.getGroup(), - polarisPropertySource.getFileName()); - - Map source = polarisPropertySource.getSource(); - - for (String changedKey : configKVFileChangeEvent.changedKeys()) { - ConfigPropertyChangeInfo configPropertyChangeInfo = configKVFileChangeEvent - .getChangeInfo(changedKey); - - LOGGER.info("[SCT Config] changed property = {}", configPropertyChangeInfo); - - switch (configPropertyChangeInfo.getChangeType()) { - case MODIFIED: - case ADDED: - source.put(changedKey, configPropertyChangeInfo.getNewValue()); - break; - case DELETED: - source.remove(changedKey); - break; - } - - if (polarisConfigProperties.getRefreshType() == RefreshType.REFLECT) { - Collection targetValues = springValueRegistry.get(beanFactory, changedKey); - if (targetValues == null || targetValues.isEmpty()) { - continue; - } - // update the attribute with @Value annotation - for (SpringValue val : targetValues) { - updateSpringValue(val); - } - } - } - - if (polarisConfigProperties.getRefreshType() == RefreshType.REFLECT) { - // update @ConfigurationProperties beans - context.publishEvent(new EnvironmentChangeEvent(context, configKVFileChangeEvent.changedKeys())); - } - else { - contextRefresher.refresh(); - } - }); - } - } - - private void updateSpringValue(SpringValue springValue) { - try { - Object value = resolvePropertyValue(springValue); - springValue.update(value); - - LOGGER.info("Auto update polaris changed value successfully, new value: {}, {}", value, - springValue); - } - catch (Throwable ex) { - LOGGER.error("Auto update polaris changed value failed, {}", springValue.toString(), ex); - } - } - - - /** - * Logic transplanted from DefaultListableBeanFactory. - * - * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, - * java.lang.String, java.util.Set, org.springframework.beans.TypeConverter) - */ - private Object resolvePropertyValue(SpringValue springValue) { - // value will never be null - Object value = placeholderHelper - .resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder()); - - if (springValue.isJson()) { - value = parseJsonValue((String) value, springValue.getTargetType()); - } - else { - value = springValue.isField() ? this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField()) : - this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), - springValue.getMethodParameter()); - } - return value; - } - - private Object parseJsonValue(String json, Class targetType) { - try { - return JacksonUtils.json2JavaBean(json, targetType); - } - catch (Throwable ex) { - LOGGER.error("Parsing json '{}' to type {} failed!", json, targetType, ex); - throw ex; - } - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ConfigurableBeanFactory) beanFactory; - } -} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisReflectConfigPropertyAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisReflectConfigPropertyAutoRefresher.java new file mode 100644 index 00000000..e597d680 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisReflectConfigPropertyAutoRefresher.java @@ -0,0 +1,137 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.util.Collection; +import java.util.Set; + +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; +import com.tencent.cloud.polaris.config.spring.property.SpringValue; +import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.BeansException; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * PolarisReflectConfigPropertyAutoRefresher to refresh config in reflect type + * we can use it by setting spring.cloud.polaris.config.refresh-type=reflect. + * + * @author lingxiao.wlx + */ +public class PolarisReflectConfigPropertyAutoRefresher extends PolarisConfigPropertyAutoRefresher + implements ApplicationContextAware { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisReflectConfigPropertyAutoRefresher.class); + + private final SpringValueRegistry springValueRegistry; + + private final PlaceholderHelper placeholderHelper; + + private ConfigurableApplicationContext context; + + private ConfigurableBeanFactory beanFactory; + + private TypeConverter typeConverter; + + public PolarisReflectConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, SpringValueRegistry springValueRegistry, + PlaceholderHelper placeholderHelper) { + super(polarisConfigProperties, polarisPropertySourceManager); + this.springValueRegistry = springValueRegistry; + this.placeholderHelper = placeholderHelper; + } + + @Override + public void refreshSpringValue(String changedKey) { + Collection targetValues = springValueRegistry.get(beanFactory, changedKey); + if (targetValues == null || targetValues.isEmpty()) { + return; + } + // update the attribute with @Value annotation + for (SpringValue val : targetValues) { + updateSpringValue(val); + } + } + + @Override + public void refreshConfigurationProperties(Set changeKeys) { + context.publishEvent(new EnvironmentChangeEvent(context, changeKeys)); + } + + private void updateSpringValue(SpringValue springValue) { + try { + Object value = resolvePropertyValue(springValue); + springValue.update(value); + + LOGGER.info("Auto update polaris changed value successfully, new value: {}, {}", value, + springValue); + } + catch (Throwable ex) { + LOGGER.error("Auto update polaris changed value failed, {}", springValue.toString(), ex); + } + } + + /** + * Logic transplanted from DefaultListableBeanFactory. + * + * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, + * java.lang.String, java.util.Set, org.springframework.beans.TypeConverter) + */ + private Object resolvePropertyValue(SpringValue springValue) { + // value will never be null + Object value = placeholderHelper + .resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder()); + + if (springValue.isJson()) { + value = parseJsonValue((String) value, springValue.getTargetType()); + } + else { + value = springValue.isField() ? this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField()) : + this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), + springValue.getMethodParameter()); + } + return value; + } + + private Object parseJsonValue(String json, Class targetType) { + try { + return JacksonUtils.json2JavaBean(json, targetType); + } + catch (Throwable ex) { + LOGGER.error("Parsing json '{}' to type {} failed!", json, targetType, ex); + throw ex; + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.context = (ConfigurableApplicationContext) applicationContext; + this.beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + this.typeConverter = this.beanFactory.getTypeConverter(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshContextConfigPropertyAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshContextConfigPropertyAutoRefresher.java new file mode 100644 index 00000000..b3a79a08 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshContextConfigPropertyAutoRefresher.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.util.Set; + +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; + +import org.springframework.cloud.context.refresh.ContextRefresher; + +/** + * PolarisRefreshContextConfigPropertyAutoRefresher refresh config by refreshContext. + * + * @author lingxiao.wlx + */ +public class PolarisRefreshContextConfigPropertyAutoRefresher extends PolarisConfigPropertyAutoRefresher { + + private final ContextRefresher contextRefresher; + + public PolarisRefreshContextConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, + ContextRefresher contextRefresher) { + super(polarisConfigProperties, polarisPropertySourceManager); + this.contextRefresher = contextRefresher; + } + + @Override + public void refreshSpringValue(String changedKey) { + // do nothing,all config will be refreshed by contextRefresher.refresh + } + + @Override + public void refreshConfigurationProperties(Set changeKeys) { + contextRefresher.refresh(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabled.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabled.java new file mode 100644 index 00000000..05c80dfa --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabled.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.condition; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; + +/** + * When the refresh type is reflect, load the beans required by the reflect mode. + * + * @author lingxiao.wlx + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@Conditional(ConfigReflectCondition.class) +public @interface ConditionalOnConfigReflectEnabled { +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConfigReflectCondition.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConfigReflectCondition.java new file mode 100644 index 00000000..f75fdd4c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConfigReflectCondition.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.condition; + +import java.util.Objects; + +import com.tencent.cloud.polaris.config.enums.RefreshType; + +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * ConfigReflectCondition used by {@link ConditionalOnConfigReflectEnabled}. + * + * @author lingxiao.wlx + */ +public class ConfigReflectCondition extends SpringBootCondition { + + /** + * Refresh type config. + */ + public static final String POLARIS_CONFIG_REFRESH_TYPE = "spring.cloud.polaris.config.refresh-type"; + + /** + * Reflect refresh type value. + */ + private static final RefreshType REFLECT_TYPE = RefreshType.REFLECT; + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + RefreshType refreshType = context.getEnvironment().getProperty(POLARIS_CONFIG_REFRESH_TYPE, RefreshType.class); + if (!Objects.isNull(refreshType) && REFLECT_TYPE == refreshType) { + return ConditionOutcome.match("matched"); + } + return ConditionOutcome.noMatch("no matched"); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java index 03945edc..a2fdef9f 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java @@ -26,7 +26,6 @@ import java.util.Map; import com.google.common.collect.Lists; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; -import com.tencent.cloud.polaris.config.enums.RefreshType; import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; import com.tencent.cloud.polaris.config.spring.property.SpringValue; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; @@ -41,7 +40,6 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.context.ConfigurableApplicationContext; import static org.mockito.ArgumentMatchers.any; @@ -49,7 +47,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * test for {@link PolarisPropertySourceAutoRefresher}. + * test for {@link PolarisReflectConfigPropertyAutoRefresher}. * * @author lepdou 2022-06-11 */ @@ -63,8 +61,6 @@ public class PolarisPropertiesSourceAutoRefresherTest { private PolarisConfigProperties polarisConfigProperties; @Mock private PolarisPropertySourceManager polarisPropertySourceManager; - @Mock - private ContextRefresher contextRefresher; @Mock private SpringValueRegistry springValueRegistry; @@ -74,10 +70,8 @@ public class PolarisPropertiesSourceAutoRefresherTest { @Test public void testConfigFileChanged() throws Exception { - PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties, - polarisPropertySourceManager, springValueRegistry, placeholderHelper, contextRefresher); - - when(polarisConfigProperties.getRefreshType()).thenReturn(RefreshType.REFLECT); + PolarisReflectConfigPropertyAutoRefresher refresher = new PolarisReflectConfigPropertyAutoRefresher(polarisConfigProperties, + polarisPropertySourceManager, springValueRegistry, placeholderHelper); ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class); ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class); TypeConverter typeConverter = mock(TypeConverter.class); diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabledTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabledTest.java new file mode 100644 index 00000000..c8a14d07 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnConfigReflectEnabledTest.java @@ -0,0 +1,83 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.condition; + +import com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration; +import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.adapter.PolarisReflectConfigPropertyAutoRefresher; +import com.tencent.cloud.polaris.config.adapter.PolarisRefreshContextConfigPropertyAutoRefresher; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.config.enums.RefreshType; +import com.tencent.cloud.polaris.config.spring.annotation.SpringValueProcessor; +import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; +import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.context.refresh.ContextRefresher; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link ConditionalOnConfigReflectEnabled}. + * + * @author lingxiao.wlx + */ +public class ConditionalOnConfigReflectEnabledTest { + + @Test + public void testReflectEnabled() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + "conditionalOnConfigReflectEnabledTest") + .withPropertyValues("server.port=" + 8080) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues("spring.cloud.polaris.config.refresh-type=" + RefreshType.REFLECT) + .withPropertyValues("spring.cloud.polaris.config.enabled=true"); + contextRunner.run(context -> { + assertThat(context).hasSingleBean(PlaceholderHelper.class); + assertThat(context).hasSingleBean(SpringValueRegistry.class); + assertThat(context).hasSingleBean(SpringValueProcessor.class); + assertThat(context).hasSingleBean(PolarisReflectConfigPropertyAutoRefresher.class); + }); + } + + @Test + public void testWithoutReflectEnabled() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + "conditionalOnConfigReflectEnabledTest") + .withPropertyValues("server.port=" + 8080) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues("spring.cloud.polaris.config.enabled=true"); + contextRunner.run(context -> { + assertThat(context).hasSingleBean(PolarisConfigProperties.class); + assertThat(context).hasSingleBean(PolarisPropertySourceManager.class); + assertThat(context).hasSingleBean(ContextRefresher.class); + assertThat(context).hasSingleBean(PolarisRefreshContextConfigPropertyAutoRefresher.class); + }); + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java index b2aa08cd..28e7b4a2 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java @@ -38,7 +38,7 @@ import static org.mockito.Mockito.when; /** - * test for {@link StaticMetadataManager} + * test for {@link StaticMetadataManager}. *@author lepdou 2022-06-27 */ @RunWith(MockitoJUnitRunner.class)