entry : newConfigFileMetadataMap.entrySet()) {
+ if (!oldConfigFileMetadataMap.containsKey(entry.getKey())) {
+ added.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return added;
}
/**
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigRefreshScopeAnnotationDetector.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigRefreshScopeAnnotationDetector.java
deleted file mode 100644
index 20381a49e..000000000
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigRefreshScopeAnnotationDetector.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.lang.annotation.Annotation;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.config.BeanPostProcessor;
-import org.springframework.core.Ordered;
-import org.springframework.core.PriorityOrdered;
-import org.springframework.lang.NonNull;
-
-/**
- * Mainly used to detect whether the annotation class {@link org.springframework.cloud.context.config.annotation.RefreshScope}
- * exists, and whether the user has configured beans using this annotation in their business system.
- * If the annotation {@code @RefreshScope} exists and is used, the auto-optimization will be triggered
- * in listener {@link com.tencent.cloud.polaris.config.listener.PolarisConfigRefreshOptimizationListener}.
- *
- * This bean will only be created and initialized when the config refresh type is {@code RefreshType.REFLECT}.
- *
- * @author jarvisxiong
- */
-@SuppressWarnings({"unchecked", "rawtypes"})
-public class PolarisConfigRefreshScopeAnnotationDetector implements BeanPostProcessor, InitializingBean, PriorityOrdered {
-
- private final AtomicBoolean isRefreshScopeAnnotationUsed = new AtomicBoolean(false);
-
- private Class refreshScopeAnnotationClass;
-
- private String annotatedRefreshScopeBeanName;
-
- @Override
- public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName)
- throws BeansException {
- return bean;
- }
-
- @Override
- public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName)
- throws BeansException {
- if (isRefreshScopeAnnotationUsed() || refreshScopeAnnotationClass == null) {
- return bean;
- }
- Annotation[] refreshScopeAnnotations = bean.getClass().getAnnotationsByType(refreshScopeAnnotationClass);
- if (refreshScopeAnnotations.length > 0) {
- if (isRefreshScopeAnnotationUsed.compareAndSet(false, true)) {
- annotatedRefreshScopeBeanName = beanName;
- }
- }
- return bean;
- }
-
- @Override
- public void afterPropertiesSet() {
- try {
- refreshScopeAnnotationClass = Class.forName(
- "org.springframework.cloud.context.config.annotation.RefreshScope",
- false,
- getClass().getClassLoader());
- }
- catch (ClassNotFoundException ignored) {
- }
- }
-
- @Override
- public int getOrder() {
- return Ordered.LOWEST_PRECEDENCE;
- }
-
- public boolean isRefreshScopeAnnotationUsed() {
- return isRefreshScopeAnnotationUsed.get();
- }
-
- public String getAnnotatedRefreshScopeBeanName() {
- return annotatedRefreshScopeBeanName;
- }
-}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java
index 193285bf0..421cd41db 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java
@@ -24,6 +24,7 @@
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.ConfigFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,6 +32,7 @@
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
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.ConfigurableApplicationContext;
@@ -57,11 +59,15 @@ public class PolarisRefreshAffectedContextRefresher extends PolarisConfigPropert
private TypeConverter typeConverter;
+ private ContextRefresher contextRefresher;
+
public PolarisRefreshAffectedContextRefresher(PolarisConfigProperties polarisConfigProperties,
- SpringValueRegistry springValueRegistry, PlaceholderHelper placeholderHelper) {
- super(polarisConfigProperties);
+ SpringValueRegistry springValueRegistry, PlaceholderHelper placeholderHelper,
+ ConfigFileService configFileService, ContextRefresher contextRefresher) {
+ super(polarisConfigProperties, configFileService);
this.springValueRegistry = springValueRegistry;
this.placeholderHelper = placeholderHelper;
+ this.contextRefresher = contextRefresher;
}
@Override
@@ -78,7 +84,20 @@ public void refreshSpringValue(String changedKey) {
@Override
public void refreshConfigurationProperties(Set changeKeys) {
- context.publishEvent(new EnvironmentChangeEvent(context, changeKeys));
+ boolean needRefreshContext = false;
+ for (String changedKey : changeKeys) {
+ boolean inRefreshScope = springValueRegistry.isRefreshScopeKey(changedKey);
+ if (inRefreshScope) {
+ needRefreshContext = true;
+ break;
+ }
+ }
+ if (needRefreshContext) {
+ contextRefresher.refresh();
+ }
+ else {
+ context.publishEvent(new EnvironmentChangeEvent(context, changeKeys));
+ }
}
private void updateSpringValue(SpringValue springValue) {
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java
index 1f51885c3..ea2f55087 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java
@@ -13,6 +13,7 @@
* 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;
@@ -20,8 +21,15 @@
import java.util.Set;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
+import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
+import com.tencent.polaris.configuration.api.core.ConfigFileService;
+import org.springframework.beans.BeansException;
+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.ConfigurableApplicationContext;
/**
* The default implement of Spring Cloud refreshes the entire Spring Context.
@@ -29,13 +37,19 @@
*
* @author lingxiao.wlx
*/
-public class PolarisRefreshEntireContextRefresher extends PolarisConfigPropertyAutoRefresher {
+public class PolarisRefreshEntireContextRefresher extends PolarisConfigPropertyAutoRefresher implements ApplicationContextAware {
private final ContextRefresher contextRefresher;
+ private final SpringValueRegistry springValueRegistry;
+
+ private ConfigurableApplicationContext context;
+
public PolarisRefreshEntireContextRefresher(PolarisConfigProperties polarisConfigProperties,
- ContextRefresher contextRefresher) {
- super(polarisConfigProperties);
+ SpringValueRegistry springValueRegistry, ConfigFileService configFileService, ContextRefresher contextRefresher) {
+
+ super(polarisConfigProperties, configFileService);
+ this.springValueRegistry = springValueRegistry;
this.contextRefresher = contextRefresher;
}
@@ -46,6 +60,24 @@ public void refreshSpringValue(String changedKey) {
@Override
public void refreshConfigurationProperties(Set changeKeys) {
- contextRefresher.refresh();
+ boolean needRefreshContext = false;
+ for (String changedKey : changeKeys) {
+ boolean inRefreshScope = springValueRegistry.isRefreshScopeKey(changedKey);
+ if (inRefreshScope) {
+ needRefreshContext = true;
+ break;
+ }
+ }
+ if (needRefreshContext) {
+ contextRefresher.refresh();
+ }
+ else {
+ context.publishEvent(new EnvironmentChangeEvent(context, changeKeys));
+ }
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.context = (ConfigurableApplicationContext) applicationContext;
}
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java
index 15186d32e..39c8442c9 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java
@@ -36,7 +36,7 @@ public class ReflectRefreshTypeCondition extends SpringBootCondition {
*/
public static final String POLARIS_CONFIG_REFRESH_TYPE = "spring.cloud.polaris.config.refresh-type";
- private static final RefreshType DEFAULT_REFRESH_TYPE = RefreshType.REFLECT;
+ private static final RefreshType DEFAULT_REFRESH_TYPE = RefreshType.REFRESH_CONTEXT;
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java
index 3dc62ed12..2b330ea19 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java
@@ -69,7 +69,7 @@ public class PolarisConfigProperties {
/**
* Attribute refresh type.
*/
- private RefreshType refreshType = RefreshType.REFLECT;
+ private RefreshType refreshType = RefreshType.REFRESH_CONTEXT;
/**
* List of injected configuration files.
@@ -95,6 +95,11 @@ public class PolarisConfigProperties {
*/
private boolean internalEnabled = true;
+ /**
+ * if check address is enabled.
+ */
+ private boolean checkAddress = true;
+
public boolean isEnabled() {
return enabled;
}
@@ -191,6 +196,14 @@ public void setInternalEnabled(boolean internalEnabled) {
this.internalEnabled = internalEnabled;
}
+ public boolean isCheckAddress() {
+ return checkAddress;
+ }
+
+ public void setCheckAddress(boolean checkAddress) {
+ this.checkAddress = checkAddress;
+ }
+
@Override
public String toString() {
return "PolarisConfigProperties{" +
@@ -206,6 +219,7 @@ public String toString() {
", dataSource='" + dataSource + '\'' +
", localFileRootPath='" + localFileRootPath + '\'' +
", internalEnabled=" + internalEnabled +
+ ", checkAddress=" + checkAddress +
'}';
}
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListener.java
deleted file mode 100644
index 4badbc157..000000000
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListener.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.listener;
-
-import java.util.Collections;
-
-import com.tencent.cloud.polaris.config.adapter.PolarisConfigRefreshScopeAnnotationDetector;
-import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher;
-import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
-import com.tencent.cloud.polaris.config.enums.RefreshType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.config.ConstructorArgumentValues;
-import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.cloud.context.refresh.ContextRefresher;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.event.ContextRefreshedEvent;
-import org.springframework.core.env.MapPropertySource;
-import org.springframework.core.env.MutablePropertySources;
-import org.springframework.lang.NonNull;
-
-import static com.tencent.cloud.polaris.config.condition.ReflectRefreshTypeCondition.POLARIS_CONFIG_REFRESH_TYPE;
-
-/**
- * When {@link com.tencent.cloud.polaris.config.adapter.PolarisConfigRefreshScopeAnnotationDetector} detects that
- * the annotation {@code @RefreshScope} exists and is used, but the config refresh type
- * {@code spring.cloud.polaris.config.refresh-type} is still {@code RefreshType.REFLECT}, then the framework will
- * automatically switch the config refresh type to {@code RefreshType.REFRESH_CONTEXT}.
- *
- * The purpose of this optimization is to omit additional configuration, and facilitate for users to use the
- * dynamic configuration refresh strategy of Spring Cloud Context.
- *
- * @author jarvisxiong
- */
-public class PolarisConfigRefreshOptimizationListener implements ApplicationListener {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigRefreshOptimizationListener.class);
-
- private static final String CONFIG_REFRESH_TYPE_PROPERTY = "configRefreshTypeProperty";
-
- private static final String REFLECT_REBINDER_BEAN_NAME = "affectedConfigurationPropertiesRebinder";
-
- private static final String REFLECT_REFRESHER_BEAN_NAME = "polarisReflectPropertySourceAutoRefresher";
-
- private static final String REFRESH_CONTEXT_REFRESHER_BEAN_NAME = "polarisRefreshContextPropertySourceAutoRefresher";
-
-
- @Override
- public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
- ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) event.getApplicationContext();
- PolarisConfigRefreshScopeAnnotationDetector detector = applicationContext.getBean(PolarisConfigRefreshScopeAnnotationDetector.class);
- boolean isRefreshScopeAnnotationUsed = detector.isRefreshScopeAnnotationUsed();
- String annotatedRefreshScopeBeanName = detector.getAnnotatedRefreshScopeBeanName();
- // using System.setProperty to set spring.cloud.polaris.config.refresh-type
- String value = System.getProperty("spring.cloud.polaris.config.refresh-type");
- boolean isSystemSetRefreshType = RefreshType.REFRESH_CONTEXT.toString().equalsIgnoreCase(value);
- // a bean is using @RefreshScope, but the config refresh type is still [reflect], switch automatically
- if (isRefreshScopeAnnotationUsed || isSystemSetRefreshType) {
- if (isRefreshScopeAnnotationUsed) {
- LOGGER.warn("Detected that the bean [{}] is using @RefreshScope annotation, but the config refresh type is still [reflect]. " + "[SCT] will automatically switch to [refresh_context].", annotatedRefreshScopeBeanName);
- }
- if (isSystemSetRefreshType) {
- LOGGER.warn("Detected that using System.setProperty to set spring.cloud.polaris.config.refresh-type = refresh_context, but the config refresh type is still [reflect]. " + "[SCT] will automatically switch to [refresh_context].");
- }
- switchConfigRefreshTypeProperty(applicationContext);
- modifyPolarisConfigPropertiesBean(applicationContext);
- // remove related bean of type [reflect]
- removeRelatedBeansOfReflect(applicationContext);
- // register a new refresher bean of type [refresh_context]
- registerRefresherBeanOfRefreshContext(applicationContext);
- // add the new refresher to context as a listener
- addRefresherBeanAsListener(applicationContext);
- }
- }
-
- private void switchConfigRefreshTypeProperty(ConfigurableApplicationContext applicationContext) {
- MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
- propertySources.addFirst(new MapPropertySource(CONFIG_REFRESH_TYPE_PROPERTY, Collections.singletonMap(POLARIS_CONFIG_REFRESH_TYPE, RefreshType.REFRESH_CONTEXT)));
- }
-
- private void modifyPolarisConfigPropertiesBean(ConfigurableApplicationContext applicationContext) {
- PolarisConfigProperties polarisConfigProperties = applicationContext.getBean(PolarisConfigProperties.class);
- polarisConfigProperties.setRefreshType(RefreshType.REFRESH_CONTEXT);
- }
-
- private void removeRelatedBeansOfReflect(ConfigurableApplicationContext applicationContext) {
- try {
- DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
- beanFactory.removeBeanDefinition(REFLECT_REFRESHER_BEAN_NAME);
- beanFactory.removeBeanDefinition(REFLECT_REBINDER_BEAN_NAME);
- }
- catch (BeansException e) {
- // If there is a removeBean exception in this code, do not affect the main process startup. Some user usage may cause the polarisReflectPropertySourceAutoRefresher to not load, and the removeBeanDefinition will report an error
- LOGGER.debug("removeRelatedBeansOfReflect occur error:", e);
- }
- }
-
- private void registerRefresherBeanOfRefreshContext(ConfigurableApplicationContext applicationContext) {
- DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
- AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
- beanDefinition.setBeanClass(PolarisRefreshEntireContextRefresher.class);
- PolarisConfigProperties polarisConfigProperties = beanFactory.getBean(PolarisConfigProperties.class);
- ContextRefresher contextRefresher = beanFactory.getBean(ContextRefresher.class);
- ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
- constructorArgumentValues.addIndexedArgumentValue(0, polarisConfigProperties);
- constructorArgumentValues.addIndexedArgumentValue(1, contextRefresher);
- beanFactory.registerBeanDefinition(REFRESH_CONTEXT_REFRESHER_BEAN_NAME, beanDefinition);
- }
-
-
- private void addRefresherBeanAsListener(ConfigurableApplicationContext applicationContext) {
- PolarisRefreshEntireContextRefresher refresher = (PolarisRefreshEntireContextRefresher) applicationContext.getBean(REFRESH_CONTEXT_REFRESHER_BEAN_NAME);
- applicationContext.addApplicationListener(refresher);
- }
-}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java
index 08898608d..1ba9eb02d 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java
@@ -23,7 +23,11 @@
import java.util.List;
import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.lang.NonNull;
@@ -34,17 +38,32 @@
*
* @author weihubeats 2022-7-10
*/
-public abstract class PolarisProcessor implements BeanPostProcessor, PriorityOrdered {
+public abstract class PolarisProcessor implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware {
+
+ private ConfigurableListableBeanFactory beanFactory;
@Override
public Object postProcessBeforeInitialization(Object bean, @NonNull String beanName)
throws BeansException {
Class> clazz = bean.getClass();
+
+ boolean isRefreshScope = false;
+
+ try {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+ if ("refresh".equals(beanDefinition.getScope())) {
+ isRefreshScope = true;
+ }
+ }
+ catch (Exception ignored) {
+ // ignore
+ }
+
for (Field field : findAllField(clazz)) {
- processField(bean, beanName, field);
+ processField(bean, beanName, field, isRefreshScope);
}
for (Method method : findAllMethod(clazz)) {
- processMethod(bean, beanName, method);
+ processMethod(bean, beanName, method, isRefreshScope);
}
return bean;
}
@@ -60,7 +79,7 @@ public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull Stri
* @param beanName beanName
* @param field field
*/
- protected abstract void processField(Object bean, String beanName, Field field);
+ protected abstract void processField(Object bean, String beanName, Field field, boolean isRefreshScope);
/**
* subclass should implement this method to process method.
@@ -68,7 +87,7 @@ public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull Stri
* @param beanName beanName
* @param method method
*/
- protected abstract void processMethod(Object bean, String beanName, Method method);
+ protected abstract void processMethod(Object bean, String beanName, Method method, boolean isRefreshScope);
@Override
@@ -77,15 +96,20 @@ public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
- private List findAllField(Class> clazz) {
+ protected List findAllField(Class> clazz) {
final List res = new LinkedList<>();
ReflectionUtils.doWithFields(clazz, res::add);
return res;
}
- private List findAllMethod(Class> clazz) {
+ protected List findAllMethod(Class> clazz) {
final List res = new LinkedList<>();
ReflectionUtils.doWithMethods(clazz, res::add);
return res;
}
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.beanFactory = (ConfigurableListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java
index 0d79b1cc1..d925d7cfe 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java
@@ -21,11 +21,13 @@
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import com.google.common.base.CaseFormat;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
@@ -35,6 +37,7 @@
import com.tencent.cloud.polaris.config.spring.property.SpringValue;
import com.tencent.cloud.polaris.config.spring.property.SpringValueDefinition;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
+import com.tencent.polaris.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,6 +53,8 @@
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.NonNull;
@@ -105,23 +110,31 @@ public Object postProcessBeforeInitialization(Object bean, @NonNull String beanN
@Override
- protected void processField(Object bean, String beanName, Field field) {
+ protected void processField(Object bean, String beanName, Field field, boolean isRefreshScope) {
// register @Value on field
Value value = field.getAnnotation(Value.class);
if (value == null) {
return;
}
- doRegister(bean, beanName, field, value);
+ doRegister(bean, beanName, field, value, isRefreshScope);
}
@Override
- protected void processMethod(Object bean, String beanName, Method method) {
+ protected void processMethod(Object bean, String beanName, Method method, boolean isRefreshScope) {
//register @Value on method
Value value = method.getAnnotation(Value.class);
- if (value == null) {
+ if (value != null) {
+ processMethodValue(bean, beanName, method, value, isRefreshScope);
return;
}
+
+ if (method.getAnnotation(RefreshScope.class) != null) {
+ processMethodRefreshScope(bean, method);
+ }
+ }
+
+ private void processMethodValue(Object bean, String beanName, Method method, Value value, boolean isRefreshScope) {
//skip Configuration bean methods
if (method.getAnnotation(Bean.class) != null) {
return;
@@ -131,8 +144,128 @@ protected void processMethod(Object bean, String beanName, Method method) {
bean.getClass().getName(), method.getName(), method.getParameterTypes().length);
return;
}
+ doRegister(bean, beanName, method, value, isRefreshScope);
+ }
+
+ /**
+ * @RefreshScope on method.
+ * method parameter with @Value ${@link com.tencent.cloud.polaris.config.spring.annotation.RefreshScopeSpringProcessorTest.TestConfig3#testBean3}.
+ * method parameter class with @ConfigurationProperties ${@link com.tencent.cloud.polaris.config.spring.annotation.RefreshScopeSpringProcessorTest.TestConfig4#testBean4}.
+ * @ConfigurationProperties outside method may effect @RefreshScope bean${@link com.tencent.cloud.polaris.config.spring.annotation.RefreshScopeSpringProcessorTest.TestConfig5#testBean5()}.
+ * @param bean spring bean.
+ * @param method method.
+ */
+ private void processMethodRefreshScope(Object bean, Method method) {
+ // must have @Bean annotation
+ if (method.getAnnotation(Bean.class) == null) {
+ return;
+ }
+
+ for (Parameter parameter : method.getParameters()) {
+ Value value = parameter.getAnnotation(Value.class);
+ if (value != null) {
+ // method parameter with @Value
+ Set keys = placeholderHelper.extractPlaceholderKeys(value.value());
+ springValueRegistry.putRefreshScopeKeys(keys);
+ }
+ // method parameter class with @ConfigurationProperties
+ ConfigurationProperties configurationProperties = parameter.getType().getAnnotation(ConfigurationProperties.class);
+ parseConfigurationPropertiesKeys(configurationProperties, parameter.getType());
+ }
+
+ // analyze all fields of the class containing the method.
+ for (Field field : findAllField(bean.getClass())) {
+ Value value = field.getAnnotation(Value.class);
+ if (value != null) {
+ // field with @Value
+ Set keys = placeholderHelper.extractPlaceholderKeys(value.value());
+ springValueRegistry.putRefreshScopeKeys(keys);
+ continue;
+ }
+ // field class with @ConfigurationProperties
+ ConfigurationProperties configurationProperties = field.getType().getAnnotation(ConfigurationProperties.class);
+ parseConfigurationPropertiesKeys(configurationProperties, field.getType());
+ }
+ }
+
+ /**
+ * parse refresh scope keys from @ConfigurationProperties.
+ * @param configurationProperties @ConfigurationProperties annotation object.
+ * @param clazz class of @ConfigurationProperties bean.
+ */
+ private void parseConfigurationPropertiesKeys(ConfigurationProperties configurationProperties, Class> clazz) {
+ if (configurationProperties != null) {
+ // get prefix from @ConfigurationProperties prefix or value.
+ String prefix = configurationProperties.value();
+ if (StringUtils.isEmpty(prefix)) {
+ prefix = configurationProperties.prefix();
+ }
+ if (StringUtils.isNotEmpty(prefix)) {
+ prefix += ".";
+ }
+ parseConfigKeys(clazz, prefix);
+ }
+ }
+
+ /**
+ * parse all fields of the configClazz.
+ * if the field is primitive or wrapper, add it to refresh scope key map.
+ * ${@link com.tencent.cloud.polaris.config.spring.annotation.RefreshScopeSpringProcessorTest.TestBeanProperties2#name}
+ * if the field is collection, add it to refresh scope prefix trie node.
+ * ${@link com.tencent.cloud.polaris.config.spring.annotation.RefreshScopeSpringProcessorTest.TestBeanProperties2#list}
+ * if the field is complex type, recursive parse.
+ * ${@link com.tencent.cloud.polaris.config.spring.annotation.RefreshScopeSpringProcessorTest.TestBeanProperties2#inner}
+ * @param configClazz class or subclass of @ConfigurationProperties bean.
+ * @param prefix prefix or subclass's prefix of @ConfigurationProperties bean.
+ */
+ private void parseConfigKeys(Class> configClazz, String prefix) {
+ for (Field field : findAllField(configClazz)) {
+ if (isPrimitiveOrWrapper(field.getType())) {
+ // lowerCamel format
+ springValueRegistry.putRefreshScopeKey(prefix + field.getName());
+ // lower-hyphen format
+ springValueRegistry.putRefreshScopeKey(
+ prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, field.getName()));
+ }
+ else if (isCollection(field.getType())) {
+ springValueRegistry.putRefreshScopePrefixKey(prefix + field.getName());
+ springValueRegistry.putRefreshScopePrefixKey(
+ prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, field.getName()));
+ }
+ else {
+ // complex type, recursive parse
+ parseConfigKeys(field.getType(), prefix + field.getName() + ".");
+ parseConfigKeys(field.getType(),
+ prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, field.getName()) + ".");
+ }
+ }
+ }
- doRegister(bean, beanName, method, value);
+ /**
+ * whether the class is primitive or wrapper.
+ * @param clazz the class under analysis.
+ * @return true if the class is primitive or wrapper, otherwise false.
+ */
+ private static boolean isPrimitiveOrWrapper(Class> clazz) {
+ return clazz.isPrimitive() ||
+ clazz == String.class ||
+ clazz == Boolean.class ||
+ clazz == Character.class ||
+ clazz == Byte.class ||
+ clazz == Short.class ||
+ clazz == Integer.class ||
+ clazz == Long.class ||
+ clazz == Float.class ||
+ clazz == Double.class;
+ }
+
+ /**
+ * whether the class is collection(array, collection, map).
+ * @param clazz the class under analysis.
+ * @return true if the class is collection(array, collection, map), otherwise false.
+ */
+ private static boolean isCollection(Class> clazz) {
+ return clazz.isArray() || Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz);
}
@Override
@@ -147,7 +280,7 @@ public void postProcessBeanDefinitionRegistry(@NonNull BeanDefinitionRegistry be
}
}
- private void doRegister(Object bean, String beanName, Member member, Value value) {
+ private void doRegister(Object bean, String beanName, Member member, Value value, boolean isRefreshScope) {
Set keys = placeholderHelper.extractPlaceholderKeys(value.value());
if (keys.isEmpty()) {
return;
@@ -158,10 +291,16 @@ private void doRegister(Object bean, String beanName, Member member, Value value
if (member instanceof Field) {
Field field = (Field) member;
springValue = new SpringValue(key, value.value(), bean, beanName, field);
+ if (isRefreshScope) {
+ springValueRegistry.putRefreshScopeKey(key);
+ }
}
else if (member instanceof Method) {
Method method = (Method) member;
springValue = new SpringValue(key, value.value(), bean, beanName, method);
+ if (isRefreshScope) {
+ springValueRegistry.putRefreshScopeKey(key);
+ }
}
else {
LOGGER.error("Polaris @Value annotation currently only support to be used on methods and fields, "
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueRegistry.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueRegistry.java
index 8718bc264..3ac250960 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueRegistry.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueRegistry.java
@@ -20,6 +20,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -29,6 +30,9 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import com.tencent.polaris.api.pojo.TrieNode;
+import com.tencent.polaris.api.utils.TrieUtil;
import com.tencent.polaris.client.util.NamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +58,10 @@ public class SpringValueRegistry implements DisposableBean {
private final Object LOCK = new Object();
private ScheduledExecutorService executor;
+ private final TrieNode refreshScopePrefixRoot = new TrieNode<>(TrieNode.ROOT_PATH);
+
+ private final Set refreshScopeKeys = Sets.newConcurrentHashSet();
+
public void register(BeanFactory beanFactory, String key, SpringValue springValue) {
if (!registry.containsKey(beanFactory)) {
synchronized (LOCK) {
@@ -63,7 +71,16 @@ public void register(BeanFactory beanFactory, String key, SpringValue springValu
}
}
- registry.get(beanFactory).put(key, springValue);
+ Multimap multimap = registry.get(beanFactory);
+ for (SpringValue existingValue : multimap.get(key)) {
+ // if the spring value is already registered, remove it
+ if (existingValue.getBeanName().equals(springValue.getBeanName())) {
+ multimap.remove(key, existingValue);
+ break;
+ }
+
+ }
+ multimap.put(key, springValue);
// lazy initialize
if (initialized.compareAndSet(false, true)) {
@@ -102,6 +119,30 @@ private void scanAndClean() {
}
}
+ public void putRefreshScopePrefixKey(String key) {
+ TrieUtil.buildConfigTrieNode(key, refreshScopePrefixRoot);
+ }
+
+ public void putRefreshScopeKey(String key) {
+ refreshScopeKeys.add(key);
+ }
+
+ public void putRefreshScopeKeys(Set keys) {
+ refreshScopeKeys.addAll(keys);
+ }
+
+ /**
+ * first check if the key is in refreshScopeKeys, if not, check the key by TrieUtil.
+ * @param key changed key.
+ * @return true if the key is refresh scope key, otherwise false.
+ */
+ public boolean isRefreshScopeKey(String key) {
+ if (refreshScopeKeys.contains(key)) {
+ return true;
+ }
+ return TrieUtil.checkConfig(refreshScopePrefixRoot, key);
+ }
+
@Override
public void destroy() throws Exception {
executor.shutdown();
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java
index f423ea86b..5d19faec4 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java
@@ -36,12 +36,32 @@
public class MockedConfigKVFile implements ConfigKVFile {
private final Map properties;
+
+ private String fileName;
+
+ private String fileGroup;
+
+ private String namespace;
+
private final List listeners = new ArrayList<>();
public MockedConfigKVFile(Map properties) {
this.properties = properties;
}
+ public MockedConfigKVFile(Map properties, String fileName) {
+ this.properties = properties;
+ this.fileName = fileName;
+ }
+
+ public MockedConfigKVFile(Map properties, String fileName, String fileGroup, String namespace) {
+ this.properties = properties;
+ this.fileName = fileName;
+ this.fileGroup = fileGroup;
+ this.namespace = namespace;
+
+ }
+
@Override
public String getProperty(String s, String s1) {
return String.valueOf(properties.get(s));
@@ -160,16 +180,16 @@ public void fireChangeListener(ConfigKVFileChangeEvent event) {
@Override
public String getNamespace() {
- return null;
+ return namespace;
}
@Override
public String getFileGroup() {
- return null;
+ return fileGroup;
}
@Override
public String getFileName() {
- return null;
+ return fileName;
}
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java
index d9a215b70..3e8e5fb09 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java
@@ -17,6 +17,8 @@
package com.tencent.cloud.polaris.config.adapter;
+import java.lang.reflect.Field;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -25,24 +27,34 @@
import com.google.common.collect.Lists;
import com.tencent.cloud.polaris.config.config.ConfigFileGroup;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
+import com.tencent.cloud.polaris.config.enums.RefreshType;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
+import com.tencent.polaris.configuration.client.internal.RevisableConfigFileGroup;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
+import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
/**
* test for {@link PolarisConfigFileLocator}.
- *@author lepdou 2022-06-11
+ *
+ * @author lepdou 2022-06-11
*/
@ExtendWith(MockitoExtension.class)
public class PolarisConfigFileLocatorTest {
@@ -65,6 +77,7 @@ public void setUp() {
@Test
public void testLoadApplicationPropertiesFile() {
+ clearCompositePropertySourceCache();
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, environment);
@@ -102,6 +115,7 @@ public void testLoadApplicationPropertiesFile() {
@Test
public void testActiveProfileFilesPriorityBiggerThanDefault() {
+ clearCompositePropertySourceCache();
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, environment);
@@ -151,6 +165,7 @@ public void testActiveProfileFilesPriorityBiggerThanDefault() {
@Test
public void testGetCustomFiles() {
+ clearCompositePropertySourceCache();
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, environment);
@@ -201,4 +216,166 @@ public void testGetCustomFiles() {
assertThat(propertySource.getProperty("k2")).isEqualTo("v2");
assertThat(propertySource.getProperty("k3")).isEqualTo("v3");
}
+
+
+ @Test
+ public void testGetCustomGroupFiles() {
+ clearCompositePropertySourceCache();
+ PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
+ configFileService, environment);
+
+ when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
+ when(polarisContextProperties.getService()).thenReturn(testServiceName);
+
+ Map emptyMap = new HashMap<>();
+ ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
+
+ when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties")).thenReturn(emptyConfigFile);
+ when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
+ when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yaml")).thenReturn(emptyConfigFile);
+ when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
+ when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
+ when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yaml")).thenReturn(emptyConfigFile);
+
+ List customFiles = new LinkedList<>();
+ ConfigFileGroup configFileGroup = new ConfigFileGroup();
+ String customGroup = "group2";
+ configFileGroup.setName(customGroup);
+ String customFile1 = "file1.properties";
+ String customFile2 = "file2.yaml";
+ customFiles.add(configFileGroup);
+
+ when(polarisConfigProperties.isEnabled()).thenReturn(true);
+ when(polarisConfigProperties.getGroups()).thenReturn(customFiles);
+ when(polarisConfigProperties.isInternalEnabled()).thenReturn(true);
+ when(environment.getActiveProfiles()).thenReturn(new String[] {});
+
+ // file1.properties
+ Map file1Map = new HashMap<>();
+ file1Map.put("k1", "v1");
+ file1Map.put("k2", "v2");
+ ConfigKVFile file1 = new MockedConfigKVFile(file1Map, customFile1);
+ when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile1)).thenReturn(file1);
+
+ // file2.yaml
+ Map file2Map = new HashMap<>();
+ file2Map.put("k1", "v11");
+ file2Map.put("k3", "v3");
+ ConfigKVFile file2 = new MockedConfigKVFile(file2Map, customFile2);
+ when(configFileService.getConfigYamlFile(testNamespace, customGroup, customFile2)).thenReturn(file2);
+
+ RevisableConfigFileGroup revisableConfigFileGroup = new RevisableConfigFileGroup(testNamespace, customGroup, Arrays.asList(file1, file2), "v1");
+ when(configFileService.getConfigFileGroup(testNamespace, customGroup)).thenReturn(revisableConfigFileGroup);
+
+ PropertySource> propertySource = locator.locate(environment);
+
+ assertThat(propertySource.getProperty("k1")).isEqualTo("v1");
+ assertThat(propertySource.getProperty("k2")).isEqualTo("v2");
+ assertThat(propertySource.getProperty("k3")).isEqualTo("v3");
+ }
+
+ @Test
+ void testInitTsfConfigGroupsSuccessfulLoad() {
+ clearCompositePropertySourceCache();
+
+ // Arrange
+ String tsfId = "test-id";
+ String tsfNamespace = "test-namespace";
+ String tsfGroup = "test-group";
+ String polarisNamespace = "polaris-namespace";
+
+ when(environment.getProperty("tsf_id")).thenReturn(tsfId);
+ when(environment.getProperty("tsf_namespace_name")).thenReturn(tsfNamespace);
+ when(environment.getProperty("tsf_group_name")).thenReturn(tsfGroup);
+ when(polarisContextProperties.getNamespace()).thenReturn(polarisNamespace);
+
+ String expectedAppConfigGroup = tsfId + "." + tsfGroup + ".application_config_group";
+
+ // mock polaris config properties
+ PolarisPropertySource mockPropertySource = mock(PolarisPropertySource.class);
+ when(mockPropertySource.getPropertySourceName()).thenReturn(expectedAppConfigGroup);
+
+ CompositePropertySource compositePropertySource = mock(CompositePropertySource.class);
+ try (MockedStatic mockedStatic = mockStatic(PolarisConfigFileLocator.class)) {
+ mockedStatic.when(() -> PolarisConfigFileLocator.loadGroupPolarisPropertySource(
+ eq(configFileService),
+ eq(polarisNamespace),
+ any()
+ )).thenReturn(mockPropertySource);
+
+ PolarisConfigFileLocator locator = new PolarisConfigFileLocator(
+ polarisConfigProperties,
+ polarisContextProperties,
+ configFileService,
+ environment
+ );
+ // Act
+ locator.initTsfConfigGroups(compositePropertySource);
+
+ // Verify
+ List polarisPropertySources = PolarisPropertySourceManager.getAllPropertySources();
+ assertThat(polarisPropertySources.stream().map(PolarisPropertySource::getPropertySourceName).
+ filter(name -> name.equals(expectedAppConfigGroup)).count() == 1);
+ }
+ }
+
+ @Test
+ void testPolarisConfigProperties() {
+ PolarisConfigProperties testProperties = new PolarisConfigProperties();
+ boolean enabled = true;
+ String address = "127.0.0.1";
+ int port = 1234;
+ String token = "";
+ boolean autoRefresh = true;
+ RefreshType refreshType = RefreshType.REFRESH_CONTEXT;
+ List groups = new LinkedList<>();
+ boolean preference = true;
+ String dataSource = "test-data-source";
+ String localFileRootPath = "test-local-file-root-path";
+ boolean internalEnabled = true;
+ boolean checkAddress = true;
+ boolean shutdownIfConnectToConfigServerFailed = true;
+
+ testProperties.setEnabled(enabled);
+ testProperties.setAddress(address);
+ testProperties.setPort(port);
+ testProperties.setToken(token);
+ testProperties.setAutoRefresh(autoRefresh);
+ testProperties.setRefreshType(refreshType);
+ testProperties.setGroups(groups);
+ testProperties.setPreference(preference);
+ testProperties.setDataSource(dataSource);
+ testProperties.setLocalFileRootPath(localFileRootPath);
+ testProperties.setInternalEnabled(internalEnabled);
+ testProperties.setCheckAddress(checkAddress);
+ testProperties.setShutdownIfConnectToConfigServerFailed(shutdownIfConnectToConfigServerFailed);
+
+ Assertions.assertEquals(enabled, testProperties.isEnabled());
+ Assertions.assertEquals(address, testProperties.getAddress());
+ Assertions.assertEquals(port, testProperties.getPort());
+ Assertions.assertEquals(token, testProperties.getToken());
+ Assertions.assertEquals(autoRefresh, testProperties.isAutoRefresh());
+ Assertions.assertEquals(refreshType, testProperties.getRefreshType());
+ Assertions.assertEquals(groups, testProperties.getGroups());
+ Assertions.assertEquals(preference, testProperties.isPreference());
+ Assertions.assertEquals(dataSource, testProperties.getDataSource());
+ Assertions.assertEquals(localFileRootPath, testProperties.getLocalFileRootPath());
+ Assertions.assertEquals(internalEnabled, testProperties.isInternalEnabled());
+ Assertions.assertEquals(checkAddress, testProperties.isCheckAddress());
+ Assertions.assertEquals(shutdownIfConnectToConfigServerFailed, testProperties.isShutdownIfConnectToConfigServerFailed());
+
+ Assertions.assertNotNull(testProperties.toString());
+ }
+
+ private void clearCompositePropertySourceCache() {
+ try {
+ Class> clazz = PolarisConfigFileLocator.class;
+ Field field = clazz.getDeclaredField("compositePropertySourceCache");
+ field.setAccessible(true);
+ field.set(null, null);
+ }
+ catch (Exception e) {
+ // ignore
+ }
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigRefreshScopeAnnotationDetectorTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigRefreshScopeAnnotationDetectorTest.java
deleted file mode 100644
index 10f1bd7d8..000000000
--- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigRefreshScopeAnnotationDetectorTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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 com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration;
-import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration;
-import org.assertj.core.api.InstanceOfAssertFactories;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration;
-import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
-import org.springframework.cloud.context.config.annotation.RefreshScope;
-
-import static org.assertj.core.api.Assertions.as;
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * test for {@link PolarisConfigRefreshScopeAnnotationDetector}.
- */
-@SuppressWarnings("rawtypes")
-public class PolarisConfigRefreshScopeAnnotationDetectorTest {
-
- private static Class refreshScopeAnnotationClass = null;
-
- static {
- try {
- refreshScopeAnnotationClass = Class.forName(
- "org.springframework.cloud.context.config.annotation.RefreshScope",
- false,
- PolarisConfigRefreshScopeAnnotationDetectorTest.class.getClassLoader());
- }
- catch (ClassNotFoundException ignored) {
- }
- }
-
- @Test
- public void testUseRefreshScope() {
- ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class))
- .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class))
- .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
- .withConfiguration(AutoConfigurations.of(ConfigurationPropertiesRebinderAutoConfiguration.class))
- .withBean("testBeanWithRefreshScope", TestBeanWithRefreshScope.class)
- .withPropertyValues("spring.application.name=" + "polarisConfigRefreshScopeAnnotationDetectorTest")
- .withPropertyValues("server.port=" + 8080)
- .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
- .withPropertyValues("spring.cloud.polaris.config.connect-remote-server=false");
- contextRunner.run(context -> {
- assertThat(context).hasSingleBean(PolarisConfigRefreshScopeAnnotationDetector.class);
- PolarisConfigRefreshScopeAnnotationDetector detector = context.getBean(PolarisConfigRefreshScopeAnnotationDetector.class);
- assertThat(detector.isRefreshScopeAnnotationUsed()).isTrue();
- assertThat(detector.getAnnotatedRefreshScopeBeanName()).isEqualTo("scopedTarget.testBeanWithRefreshScope");
- assertThat(detector).extracting("refreshScopeAnnotationClass", as(InstanceOfAssertFactories.type(Class.class)))
- .isEqualTo(refreshScopeAnnotationClass);
- });
- }
-
- @Test
- public void testNotUseRefreshScope() {
- ApplicationContextRunner contextRunner = new ApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class))
- .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class))
- .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
- .withConfiguration(AutoConfigurations.of(ConfigurationPropertiesRebinderAutoConfiguration.class))
- .withPropertyValues("spring.application.name=" + "polarisConfigRefreshScopeAnnotationDetectorTest")
- .withPropertyValues("server.port=" + 8080)
- .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
- .withPropertyValues("spring.cloud.polaris.config.connect-remote-server=false");
- contextRunner.run(context -> {
- assertThat(context).hasSingleBean(PolarisConfigRefreshScopeAnnotationDetector.class);
- PolarisConfigRefreshScopeAnnotationDetector detector = context.getBean(PolarisConfigRefreshScopeAnnotationDetector.class);
- assertThat(detector.isRefreshScopeAnnotationUsed()).isFalse();
- assertThat(detector.getAnnotatedRefreshScopeBeanName()).isNull();
- assertThat(detector).extracting("refreshScopeAnnotationClass", as(InstanceOfAssertFactories.type(Class.class)))
- .isEqualTo(refreshScopeAnnotationClass);
- });
- }
-
- @RefreshScope
- protected static class TestBeanWithRefreshScope {
-
- }
-}
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 7bbdd5fc5..24b5fea1b 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
@@ -19,17 +19,24 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
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 com.tencent.polaris.configuration.api.core.ChangeType;
+import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
+import com.tencent.polaris.configuration.client.internal.CompositeConfigFile;
+import com.tencent.polaris.configuration.client.internal.RevisableConfigFileGroup;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -38,6 +45,7 @@
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.assertj.core.api.Assertions.assertThat;
@@ -54,6 +62,7 @@
public class PolarisPropertiesSourceAutoRefresherTest {
private final String testNamespace = "testNamespace";
+ private final String testFileGroup = "testFileGroup";
private final String testServiceName = "testServiceName";
private final String testFileName = "application.properties";
@Mock
@@ -65,6 +74,12 @@ public class PolarisPropertiesSourceAutoRefresherTest {
@Mock
private PlaceholderHelper placeholderHelper;
+ @Mock
+ private ConfigFileService configFileService;
+
+ @Mock
+ private ContextRefresher contextRefresher;
+
@BeforeEach
public void setUp() {
PolarisPropertySourceManager.clearPropertySources();
@@ -72,7 +87,8 @@ public void setUp() {
@Test
public void testConfigFileChanged() throws Exception {
- PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(polarisConfigProperties, springValueRegistry, placeholderHelper);
+ PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(
+ polarisConfigProperties, springValueRegistry, placeholderHelper, configFileService, contextRefresher);
ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class);
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
TypeConverter typeConverter = mock(TypeConverter.class);
@@ -105,10 +121,13 @@ public void testConfigFileChanged() throws Exception {
ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED);
ConfigPropertyChangeInfo changeInfo3 = new ConfigPropertyChangeInfo("k2", "v2", null, ChangeType.DELETED);
+ ConfigPropertyChangeInfo changeInfoLogger = new ConfigPropertyChangeInfo("logging.level.root", null, "info", ChangeType.ADDED);
+
Map changeInfos = new HashMap<>();
changeInfos.put("k1", changeInfo);
changeInfos.put("k2", changeInfo3);
changeInfos.put("k4", changeInfo2);
+ changeInfos.put("logging.level.root", changeInfoLogger);
ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos);
refresher.onApplicationEvent(null);
@@ -120,4 +139,88 @@ public void testConfigFileChanged() throws Exception {
assertThat(polarisPropertySource.getProperty("k2")).isNull();
assertThat(polarisPropertySource.getProperty("k4")).isEqualTo("v4");
}
+
+ @Test
+ public void testConfigFileGroupChanged() throws Exception {
+ PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(
+ polarisConfigProperties, springValueRegistry, placeholderHelper, configFileService, contextRefresher);
+ ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class);
+ ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
+ TypeConverter typeConverter = mock(TypeConverter.class);
+ when(beanFactory.getTypeConverter()).thenReturn(typeConverter);
+ when(applicationContext.getBeanFactory()).thenReturn(beanFactory);
+ refresher.setApplicationContext(applicationContext);
+ when(typeConverter.convertIfNecessary(any(), any(), (Field) any())).thenReturn("v11");
+ Collection springValues = new ArrayList<>();
+ MockedConfigChange mockedConfigChange = new MockedConfigChange();
+ mockedConfigChange.setK1("v1");
+ Field field = mockedConfigChange.getClass().getDeclaredField("k1");
+ SpringValue springValue = new SpringValue("v1", "placeholder", mockedConfigChange, "mockedConfigChange", field);
+
+ springValues.add(springValue);
+
+ when(springValueRegistry.get(any(), any())).thenReturn(springValues);
+
+ when(polarisConfigProperties.isAutoRefresh()).thenReturn(true);
+
+ Map content = new ConcurrentHashMap<>();
+ content.put("k1", "v1");
+ content.put("k2", "v2");
+ content.put("k3", "v3");
+ MockedConfigKVFile file = new MockedConfigKVFile(content, testFileName, testFileGroup, testNamespace);
+ when(configFileService.getConfigPropertiesFile(testNamespace, testFileGroup, testFileName))
+ .thenReturn(file);
+
+ CompositeConfigFile compositeConfigFile = new CompositeConfigFile(Collections.singletonList(file));
+ PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testFileGroup, testFileName,
+ compositeConfigFile, new ConcurrentHashMap<>(content));
+
+ PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
+
+ RevisableConfigFileGroup revisableConfigFileGroup = new RevisableConfigFileGroup(testNamespace, testFileGroup, Collections.singletonList(file), "v1");
+ when(configFileService.getConfigFileGroup(testNamespace, testFileGroup)).thenReturn(revisableConfigFileGroup);
+
+ refresher.onApplicationEvent(null);
+
+ Map content2 = new ConcurrentHashMap<>();
+ content2.put("k1", "v1.1");
+ content2.put("k3.1", "v3.1");
+ MockedConfigKVFile file2 = new MockedConfigKVFile(content2, "file2.properties", testFileGroup, testNamespace);
+
+ when(configFileService.getConfigPropertiesFile(testNamespace, testFileGroup, "file2.properties"))
+ .thenReturn(file2);
+
+ revisableConfigFileGroup.updateConfigFileList(Arrays.asList(file, file2), "v2");
+ Thread.sleep(5000);
+ assertThat(polarisPropertySource.getProperty("k1")).isEqualTo("v1.1");
+ assertThat(polarisPropertySource.getProperty("k2")).isEqualTo("v2");
+ assertThat(polarisPropertySource.getProperty("k3")).isEqualTo("v3");
+ assertThat(polarisPropertySource.getProperty("k3.1")).isEqualTo("v3.1");
+
+ // delete event
+ revisableConfigFileGroup.updateConfigFileList(Collections.singletonList(file), "v3");
+ ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1.1", null, ChangeType.DELETED);
+ ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k3.1", "v3.1", null, ChangeType.DELETED);
+ ConfigPropertyChangeInfo changeInfo3 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED);
+ Map changeInfos = new TreeMap<>();
+ changeInfos.put("k1", changeInfo);
+ changeInfos.put("k3.1", changeInfo2);
+ changeInfos.put("k4", changeInfo3);
+ ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos);
+ file2.fireChangeListener(event);
+ Thread.sleep(5000);
+
+ assertThat(polarisPropertySource.getProperty("k1")).isEqualTo("v1");
+ assertThat(polarisPropertySource.getProperty("k3.1")).isNull();
+ assertThat(polarisPropertySource.getProperty("k4")).isEqualTo("v4");
+
+ revisableConfigFileGroup.updateConfigFileList(Arrays.asList(file, file2), "v4");
+ Thread.sleep(5000);
+
+ // no exception
+ MockedConfigKVFile file3 = new MockedConfigKVFile(Collections.emptyMap(), "file3.properties", testFileGroup, testNamespace);
+ revisableConfigFileGroup.updateConfigFileList(Arrays.asList(file, file2, file3), "v5");
+ Thread.sleep(5000);
+
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresherTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresherTest.java
new file mode 100644
index 000000000..dc90f2e37
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresherTest.java
@@ -0,0 +1,209 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
+import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
+import com.tencent.polaris.configuration.api.core.ConfigFileService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
+import org.springframework.cloud.context.refresh.ContextRefresher;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link PolarisRefreshEntireContextRefresher}.
+ *
+ * @author Shedfree Wu
+ */
+public class PolarisRefreshEntireContextRefresherTest {
+ @Mock
+ private PolarisConfigProperties polarisConfigProperties;
+
+ @Mock
+ private SpringValueRegistry springValueRegistry;
+
+ @Mock
+ private ConfigFileService configFileService;
+
+ @Mock
+ private ContextRefresher contextRefresher;
+
+ @Mock
+ private ConfigurableApplicationContext applicationContext;
+
+ @Mock
+ private PolarisConfigCustomExtensionLayer mockExtensionLayer;
+
+ private PolarisRefreshEntireContextRefresher refresher;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ refresher = new PolarisRefreshEntireContextRefresher(
+ polarisConfigProperties,
+ springValueRegistry,
+ configFileService,
+ contextRefresher
+ );
+ refresher.setApplicationContext(applicationContext);
+ }
+
+ @Test
+ void testRefreshSpringValue() {
+ // test refreshSpringValue method, it should do nothing
+ refresher.refreshSpringValue("test.key");
+
+ // Verify
+ verifyNoInteractions(contextRefresher);
+ verifyNoInteractions(springValueRegistry);
+ }
+
+ @Test
+ void testRefreshConfigurationPropertiesWithRefreshScope() {
+ // Arrange
+ Set changeKeys = new HashSet<>();
+ changeKeys.add("test.key1");
+ changeKeys.add("test.key2");
+
+ // mock test.key1 in refresh scope
+ when(springValueRegistry.isRefreshScopeKey("test.key1")).thenReturn(true);
+
+ // Act
+ refresher.refreshConfigurationProperties(changeKeys);
+
+ // Verify
+ verify(contextRefresher, times(1)).refresh();
+ verifyNoInteractions(applicationContext);
+ }
+
+ @Test
+ void testRefreshConfigurationPropertiesWithoutRefreshScope() {
+ // Arrange
+ Set changeKeys = new HashSet<>();
+ changeKeys.add("test.key1");
+ changeKeys.add("test.key2");
+
+ // mock a key not in refresh scope
+ when(springValueRegistry.isRefreshScopeKey(anyString())).thenReturn(false);
+
+ // Act
+ refresher.refreshConfigurationProperties(changeKeys);
+
+ // Verify
+ verify(contextRefresher, never()).refresh();
+ verify(applicationContext, times(1))
+ .publishEvent(any(EnvironmentChangeEvent.class));
+ }
+
+ @Test
+ void testSetApplicationContext() {
+ // Arrange
+ ConfigurableApplicationContext newContext = mock(ConfigurableApplicationContext.class);
+
+ // Act
+ refresher.setApplicationContext(newContext);
+
+ // Verify
+ Set changeKeys = new HashSet<>();
+ changeKeys.add("test.key");
+ when(springValueRegistry.isRefreshScopeKey(anyString())).thenReturn(false);
+
+ refresher.refreshConfigurationProperties(changeKeys);
+ verify(newContext, times(1)).publishEvent(any(EnvironmentChangeEvent.class));
+ }
+
+ @Test
+ void testRefreshConfigurationPropertiesWithEmptyChangeKeys() {
+ // Arrange
+ Set changeKeys = new HashSet<>();
+
+ // Act
+ refresher.refreshConfigurationProperties(changeKeys);
+
+ // Verify
+ verify(contextRefresher, never()).refresh();
+ verify(applicationContext, times(1))
+ .publishEvent(any(EnvironmentChangeEvent.class));
+ }
+
+ @Test
+ void testRefreshConfigurationPropertiesWithMultipleRefreshScopeKeys() {
+ // Arrange
+ Set changeKeys = new HashSet<>();
+ changeKeys.add("test.key1");
+ changeKeys.add("test.key2");
+ changeKeys.add("test.key3");
+
+ // mock multiple keys in refresh scope
+ when(springValueRegistry.isRefreshScopeKey(anyString())).thenReturn(true);
+
+ // Act
+ refresher.refreshConfigurationProperties(changeKeys);
+
+ // Verify
+ verify(contextRefresher, times(1)).refresh();
+ verifyNoInteractions(applicationContext);
+ }
+
+ @Test
+ void testPolarisConfigCustomExtensionLayer() throws Exception {
+ refresher.setRegistered(true);
+
+ Field field = PolarisConfigPropertyAutoRefresher.class
+ .getDeclaredField("polarisConfigCustomExtensionLayer");
+ field.setAccessible(true);
+ field.set(refresher, mockExtensionLayer);
+
+ Method method = PolarisConfigPropertyAutoRefresher.class
+ .getDeclaredMethod("customInitRegisterPolarisConfig", PolarisConfigPropertyAutoRefresher.class);
+ method.setAccessible(true);
+ method.invoke(refresher, refresher);
+
+
+ method = PolarisConfigPropertyAutoRefresher.class.getDeclaredMethod(
+ "customRegisterPolarisConfigPublishChangeListener",
+ PolarisPropertySource.class, PolarisPropertySource.class);
+
+ method.setAccessible(true);
+ method.invoke(refresher, null, null);
+
+ // Verify
+ verify(mockExtensionLayer, times(1)).initRegisterConfig(refresher);
+
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java
index d4d627db7..77ce8e892 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java
@@ -17,13 +17,16 @@
package com.tencent.cloud.polaris.config.listener;
+import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Sets;
+import com.tencent.cloud.polaris.config.adapter.PolarisConfigFileLocator;
import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -34,6 +37,7 @@
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.CompositePropertySource;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -46,7 +50,9 @@
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = DEFINED_PORT, classes = ConfigChangeListenerTest.TestApplication.class,
- properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml"})
+ properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml",
+ "spring.cloud.polaris.config.connect-remote-server=true", "spring.cloud.polaris.config.check-address=false"
+ })
public class ConfigChangeListenerTest {
private static final CountDownLatch hits = new CountDownLatch(2);
@@ -57,6 +63,19 @@ public class ConfigChangeListenerTest {
@Autowired
private TestApplication.TestConfig testConfig;
+ @BeforeAll
+ public static void setUp() {
+ try {
+ Class> clazz = PolarisConfigFileLocator.class;
+ Field field = clazz.getDeclaredField("compositePropertySourceCache");
+ field.setAccessible(true);
+ field.set(null, new CompositePropertySource("mock"));
+ }
+ catch (Exception e) {
+ // ignore
+ }
+ }
+
@Test
public void test() throws InterruptedException {
//before change
@@ -70,7 +89,7 @@ public void test() throws InterruptedException {
applicationEventPublisher.publishEvent(event);
//after change
- boolean ret = hits.await(5, TimeUnit.SECONDS);
+ boolean ret = hits.await(2, TimeUnit.SECONDS);
Assertions.assertThat(ret).isEqualTo(true);
Assertions.assertThat(testConfig.getChangeCnt()).isEqualTo(2);
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListenerNotTriggeredTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListenerNotTriggeredTest.java
deleted file mode 100644
index 1e258682d..000000000
--- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListenerNotTriggeredTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.listener;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import com.tencent.cloud.polaris.config.adapter.MockedConfigKVFile;
-import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource;
-import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
-import com.tencent.cloud.polaris.config.adapter.PolarisRefreshAffectedContextRefresher;
-import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
-import com.tencent.cloud.polaris.config.enums.RefreshType;
-import com.tencent.polaris.configuration.api.core.ChangeType;
-import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
-import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mockito;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.cloud.context.refresh.ContextRefresher;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Primary;
-import org.springframework.context.support.AbstractApplicationContext;
-import org.springframework.stereotype.Component;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import static com.tencent.cloud.polaris.config.condition.ReflectRefreshTypeCondition.POLARIS_CONFIG_REFRESH_TYPE;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
-
-/**
- * test for {@link PolarisConfigRefreshOptimizationListener}.
- */
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(webEnvironment = DEFINED_PORT, classes = PolarisConfigRefreshOptimizationListenerNotTriggeredTest.TestApplication.class,
- properties = {
- "server.port=48081",
- "spring.cloud.polaris.address=grpc://127.0.0.1:10081",
- "spring.cloud.polaris.config.connect-remote-server=false",
- "spring.cloud.polaris.config.refresh-type=reflect",
- "spring.config.location = classpath:application-test.yml"
- })
-public class PolarisConfigRefreshOptimizationListenerNotTriggeredTest {
-
- private static final String REFLECT_REFRESHER_BEAN_NAME = "polarisReflectPropertySourceAutoRefresher";
-
- private static final String TEST_NAMESPACE = "testNamespace";
-
- private static final String TEST_SERVICE_NAME = "testServiceName";
-
- private static final String TEST_FILE_NAME = "application.properties";
-
- @Autowired
- private ConfigurableApplicationContext context;
-
- @BeforeAll
- static void beforeAll() {
- PolarisPropertySourceManager.clearPropertySources();
- }
-
- @Test
- public void testNotSwitchConfigRefreshType() {
- RefreshType actualRefreshType = context.getEnvironment()
- .getProperty(POLARIS_CONFIG_REFRESH_TYPE, RefreshType.class);
- assertThat(actualRefreshType).isEqualTo(RefreshType.REFLECT);
- PolarisConfigProperties polarisConfigProperties = context.getBean(PolarisConfigProperties.class);
- assertThat(polarisConfigProperties.getRefreshType()).isEqualTo(RefreshType.REFLECT);
- assertThat(context.containsBean(REFLECT_REFRESHER_BEAN_NAME)).isTrue();
- PolarisRefreshAffectedContextRefresher refresher = context
- .getBean(REFLECT_REFRESHER_BEAN_NAME, PolarisRefreshAffectedContextRefresher.class);
- assertThat(((AbstractApplicationContext) context).getApplicationListeners().contains(refresher)).isTrue();
- }
-
- @Test
- public void testConfigFileChanged() {
- Map content = new HashMap<>();
- content.put("k1", "v1");
- content.put("k2", "v2");
- content.put("k3", "v3");
- MockedConfigKVFile file = new MockedConfigKVFile(content);
-
- PolarisPropertySource polarisPropertySource = new PolarisPropertySource(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_FILE_NAME,
- file, content);
- PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
-
- PolarisRefreshAffectedContextRefresher refresher = context.getBean(PolarisRefreshAffectedContextRefresher.class);
- PolarisRefreshAffectedContextRefresher spyRefresher = Mockito.spy(refresher);
-
- refresher.setRegistered(false);
- spyRefresher.onApplicationEvent(null);
-
- ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
- ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED);
- ConfigPropertyChangeInfo changeInfo3 = new ConfigPropertyChangeInfo("k2", "v2", null, ChangeType.DELETED);
- Map changeInfos = new HashMap<>();
- changeInfos.put("k1", changeInfo);
- changeInfos.put("k2", changeInfo3);
- changeInfos.put("k4", changeInfo2);
- ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos);
- file.fireChangeListener(event);
-
- ContextRefresher mockContextRefresher = context.getBean(ContextRefresher.class);
- when(mockContextRefresher.refresh()).thenReturn(event.changedKeys());
-
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshSpringValue("k1");
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshSpringValue("k2");
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshSpringValue("k4");
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshConfigurationProperties(event.changedKeys());
- }
-
- @SpringBootApplication
- protected static class TestApplication {
-
- @Primary
- @Bean
- public ContextRefresher contextRefresher() {
- return mock(ContextRefresher.class);
- }
-
- @Component
- protected static class TestBeanWithoutRefreshScope {
-
- }
- }
-}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListenerTriggeredTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListenerTriggeredTest.java
deleted file mode 100644
index 5ffa22664..000000000
--- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/PolarisConfigRefreshOptimizationListenerTriggeredTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.listener;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import com.tencent.cloud.polaris.config.adapter.MockedConfigKVFile;
-import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource;
-import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
-import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher;
-import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
-import com.tencent.cloud.polaris.config.enums.RefreshType;
-import com.tencent.polaris.configuration.api.core.ChangeType;
-import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
-import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mockito;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.cloud.context.config.annotation.RefreshScope;
-import org.springframework.cloud.context.refresh.ContextRefresher;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Primary;
-import org.springframework.context.support.AbstractApplicationContext;
-import org.springframework.stereotype.Component;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import static com.tencent.cloud.polaris.config.condition.ReflectRefreshTypeCondition.POLARIS_CONFIG_REFRESH_TYPE;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
-
-/**
- * test for {@link PolarisConfigRefreshOptimizationListener}.
- */
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(webEnvironment = DEFINED_PORT, classes = PolarisConfigRefreshOptimizationListenerTriggeredTest.TestApplication.class,
- properties = {
- "server.port=48081",
- "spring.cloud.polaris.address=grpc://127.0.0.1:10081",
- "spring.cloud.polaris.config.connect-remote-server=false",
- "spring.cloud.polaris.config.refresh-type=reflect",
- "spring.config.location = classpath:application-test.yml"
- })
-public class PolarisConfigRefreshOptimizationListenerTriggeredTest {
-
- private static final String REFRESH_CONTEXT_REFRESHER_BEAN_NAME = "polarisRefreshContextPropertySourceAutoRefresher";
-
- private static final String TEST_NAMESPACE = "testNamespace";
-
- private static final String TEST_SERVICE_NAME = "testServiceName";
-
- private static final String TEST_FILE_NAME = "application.properties";
-
- @Autowired
- private ConfigurableApplicationContext context;
-
- @BeforeEach
- public void setUp() {
- PolarisPropertySourceManager.clearPropertySources();
- }
-
- @Test
- public void testSwitchConfigRefreshType() {
- RefreshType actualRefreshType = context.getEnvironment()
- .getProperty(POLARIS_CONFIG_REFRESH_TYPE, RefreshType.class);
- assertThat(actualRefreshType).isEqualTo(RefreshType.REFRESH_CONTEXT);
- PolarisConfigProperties polarisConfigProperties = context.getBean(PolarisConfigProperties.class);
- assertThat(polarisConfigProperties.getRefreshType()).isEqualTo(RefreshType.REFRESH_CONTEXT);
- assertThat(context.containsBean(REFRESH_CONTEXT_REFRESHER_BEAN_NAME)).isTrue();
- PolarisRefreshEntireContextRefresher refresher = context
- .getBean(REFRESH_CONTEXT_REFRESHER_BEAN_NAME, PolarisRefreshEntireContextRefresher.class);
- assertThat(((AbstractApplicationContext) context).getApplicationListeners().contains(refresher)).isTrue();
- }
-
- @Test
- public void testConfigFileChanged() {
- Map content = new HashMap<>();
- content.put("k1", "v1");
- content.put("k2", "v2");
- content.put("k3", "v3");
- MockedConfigKVFile file = new MockedConfigKVFile(content);
-
- PolarisPropertySource polarisPropertySource = new PolarisPropertySource(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_FILE_NAME,
- file, content);
- PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
-
- PolarisRefreshEntireContextRefresher refresher = context.getBean(PolarisRefreshEntireContextRefresher.class);
- PolarisRefreshEntireContextRefresher spyRefresher = Mockito.spy(refresher);
-
- refresher.setRegistered(false);
- spyRefresher.onApplicationEvent(null);
-
- ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
- ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED);
- ConfigPropertyChangeInfo changeInfo3 = new ConfigPropertyChangeInfo("k2", "v2", null, ChangeType.DELETED);
- Map changeInfos = new HashMap<>();
- changeInfos.put("k1", changeInfo);
- changeInfos.put("k2", changeInfo3);
- changeInfos.put("k4", changeInfo2);
- ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos);
- file.fireChangeListener(event);
-
- ContextRefresher mockContextRefresher = context.getBean(ContextRefresher.class);
- when(mockContextRefresher.refresh()).thenReturn(event.changedKeys());
-
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshSpringValue("k1");
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshSpringValue("k2");
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshSpringValue("k4");
- Mockito.verify(spyRefresher, Mockito.times(1))
- .refreshConfigurationProperties(event.changedKeys());
- }
-
- @SpringBootApplication
- protected static class TestApplication {
-
- @Primary
- @Bean
- public ContextRefresher contextRefresher() {
- return mock(ContextRefresher.class);
- }
-
- @Component
- @RefreshScope
- protected static class TestBeanWithRefreshScope {
-
- }
- }
-}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/RefreshScopeSpringProcessorTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/RefreshScopeSpringProcessorTest.java
new file mode 100644
index 000000000..11b17db59
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/RefreshScopeSpringProcessorTest.java
@@ -0,0 +1,334 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.spring.annotation;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Objects;
+
+import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration;
+import com.tencent.cloud.polaris.config.enums.RefreshType;
+import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for {@link SpringValueProcessor}.
+ *
+ * @author Shedfree Wu
+ */
+public class RefreshScopeSpringProcessorTest {
+
+ private static ServerSocket serverSocket;
+
+ @BeforeAll
+ static void beforeAll() {
+ new Thread(() -> {
+ try {
+ serverSocket = new ServerSocket(8093);
+ serverSocket.accept();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+
+ @AfterAll
+ static void afterAll() throws IOException {
+ if (Objects.nonNull(serverSocket)) {
+ serverSocket.close();
+ }
+ }
+
+ @Test
+ public void springValueFiledProcessorTest() {
+ ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class))
+ .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
+ .withConfiguration(AutoConfigurations.of(ValueTest.class))
+ .withConfiguration(AutoConfigurations.of(TestConfig2.class))
+ .withConfiguration(AutoConfigurations.of(TestConfig3.class))
+ .withConfiguration(AutoConfigurations.of(TestConfig4.class))
+ .withConfiguration(AutoConfigurations.of(TestConfig5.class))
+ .withConfiguration(AutoConfigurations.of(TestBeanProperties1.class))
+ .withConfiguration(AutoConfigurations.of(TestBeanProperties2.class))
+ .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class))
+ .withAllowBeanDefinitionOverriding(true)
+ .withPropertyValues("spring.application.name=" + "conditionalOnConfigReflectEnabledTest")
+ .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")
+ .withPropertyValues("timeout=10000");
+ contextRunner.run(context -> {
+ SpringValueRegistry springValueRegistry = context.getBean(SpringValueRegistry.class);
+
+ assertThat(springValueRegistry.isRefreshScopeKey("key.not.exist")).isFalse();
+ // @RefreshScope on @Component bean, @Value on field
+ assertThat(springValueRegistry.isRefreshScopeKey("timeout")).isTrue();
+ // not exact match
+ assertThat(springValueRegistry.isRefreshScopeKey("timeout.test")).isFalse();
+ // @RefreshScope on @Component bean, @Value on method
+ assertThat(springValueRegistry.isRefreshScopeKey("name")).isTrue();
+ // @RefreshScope and @Bean on method, @Value on field
+ assertThat(springValueRegistry.isRefreshScopeKey("test.bean.name")).isTrue();
+ // @RefreshScope and @Bean on method, @Value on method
+ assertThat(springValueRegistry.isRefreshScopeKey("test.bean.timeout")).isTrue();
+ // @RefreshScope and @Bean on method, @Value on parameter
+ assertThat(springValueRegistry.isRefreshScopeKey("test.param.name")).isTrue();
+ // @RefreshScope and @Bean on method, @ConfigurationProperties bean on method parameter
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties1.name")).isTrue();
+ // @RefreshScope and @Bean on method, @ConfigurationProperties bean in class
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.name")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.inner.name")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.set")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.list")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.list[0]")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.array")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.array[0]")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.map")).isTrue();
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.map.key")).isTrue();
+
+ assertThat(springValueRegistry.isRefreshScopeKey("test.properties2.notExist")).isFalse();
+ // @RefreshScope and @Bean on method, @Value bean in class
+ assertThat(springValueRegistry.isRefreshScopeKey("test.bean5.name")).isTrue();
+ });
+ }
+
+
+ @Configuration
+ @EnableAutoConfiguration
+ static class PolarisConfigAutoConfiguration {
+
+ @Autowired
+ private BeanFactory beanFactory;
+
+ public BeanFactory getBeanFactory() {
+ return beanFactory;
+ }
+
+ public void setBeanFactory(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+ }
+
+ @Component
+ @RefreshScope
+ private static class ValueTest {
+
+ ValueTest() {
+ }
+
+ private static String name;
+ @Value("${timeout:1000}")
+ private int timeout;
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ @Value("${name:1000}")
+ public void setName(String name) {
+ ValueTest.name = name;
+ }
+ }
+
+ @Configuration
+ static class TestConfig2 {
+ @Bean
+ @RefreshScope
+ public TestBean testBean2() {
+ return new TestBean();
+ }
+ }
+
+ @Configuration
+ static class TestConfig3 {
+ @Bean
+ @RefreshScope
+ public TestBean testBean3(@Value("${test.param.name:}") String name) {
+ return new TestBean();
+ }
+ }
+
+ @Configuration
+ static class TestConfig4 {
+ @Bean
+ @RefreshScope
+ public TestBean testBean4(TestBeanProperties1 testBeanProperties1) {
+ return new TestBean();
+ }
+ }
+
+ @Configuration
+ static class TestConfig5 {
+
+ @Autowired
+ private TestBeanProperties2 testBeanProperties2;
+
+ @Value("${test.bean5.name:}")
+ private String name;
+
+ @Bean
+ @RefreshScope
+ public TestBean testBean5() {
+ TestBean testBean = new TestBean();
+ testBean.setName(testBeanProperties2.getName());
+ return testBean;
+ }
+ }
+
+ static class TestBean {
+
+ @Value("${test.bean.name:}")
+ private String name;
+
+ private int timeout;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getTimeout() {
+ return timeout;
+ }
+
+ @Value("${test.bean.timeout:0}")
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+ }
+
+ @Component
+ @ConfigurationProperties(prefix = "test.properties1")
+ static class TestBeanProperties1 {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ @Component
+ @ConfigurationProperties("test.properties2")
+ static class TestBeanProperties2 {
+ private String name;
+
+ private HashSet set;
+
+ private ArrayList list;
+
+ private String[] array;
+
+ private HashMap map;
+
+ private InnerProperties inner;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public HashSet getSet() {
+ return set;
+ }
+
+ public void setSet(HashSet set) {
+ this.set = set;
+ }
+
+ public ArrayList getList() {
+ return list;
+ }
+
+ public void setList(ArrayList list) {
+ this.list = list;
+ }
+
+ public String[] getArray() {
+ return array;
+ }
+
+ public void setArray(String[] array) {
+ this.array = array;
+ }
+
+ public HashMap getMap() {
+ return map;
+ }
+
+ public void setMap(HashMap map) {
+ this.map = map;
+ }
+
+ public InnerProperties getInner() {
+ return inner;
+ }
+
+ public void setInner(InnerProperties inner) {
+ this.inner = inner;
+ }
+ }
+
+ static class InnerProperties {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/PolarisContractReporter.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/PolarisContractReporter.java
index f6682b2f7..66e2e01bb 100644
--- a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/PolarisContractReporter.java
+++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/PolarisContractReporter.java
@@ -25,6 +25,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.GzipUtil;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
@@ -100,7 +101,7 @@ else if (multipleOpenApiWebFluxResource != null) {
ReportServiceContractRequest request = new ReportServiceContractRequest();
String name = polarisContractProperties.getName();
if (StringUtils.isBlank(name)) {
- name = polarisDiscoveryProperties.getService();
+ name = MetadataContext.LOCAL_SERVICE;
}
request.setName(name);
request.setNamespace(polarisDiscoveryProperties.getNamespace());
diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/filter/FilterConstant.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/filter/FilterConstant.java
index c97faee2c..87161748e 100644
--- a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/filter/FilterConstant.java
+++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/filter/FilterConstant.java
@@ -47,7 +47,7 @@ public final class FilterConstant {
/**
* Swagger resource url prefix.
*/
- public static final String SWAGGER_RESOURCE_PREFIX = "/swagger-resource/";
+ public static final String SWAGGER_RESOURCE_PREFIX = "/swagger-resource";
/**
* Swagger webjars V2 url prefix.
diff --git a/spring-cloud-starter-tencent-polaris-discovery/pom.xml b/spring-cloud-starter-tencent-polaris-discovery/pom.xml
index 6e0ec3146..35c87ef0d 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-discovery/pom.xml
@@ -18,6 +18,12 @@
com.tencent.cloud
spring-cloud-tencent-rpc-enhancement
+
+
+ spring-security-crypto
+ org.springframework.security
+
+
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java
index ee03a7d83..6ffdbe52c 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java
@@ -82,7 +82,7 @@ public List getServices() throws PolarisException {
return Collections.emptyList();
}
return polarisDiscoveryHandler.getServices().getServices().stream()
- .map(ServiceInfo::getService).collect(Collectors.toList());
+ .map(ServiceInfo::getService).distinct().collect(Collectors.toList());
}
}
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfiguration.java
index b0cc4ebf6..33a17360d 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerAutoConfiguration.java
@@ -17,26 +17,15 @@
package com.tencent.cloud.polaris.loadbalancer;
-import java.util.ArrayList;
-import java.util.List;
-
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
-import com.tencent.cloud.rpc.enhancement.instrument.resttemplate.EnhancedRestTemplateInterceptor;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
-import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
-import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
-import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
-import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.util.CollectionUtils;
/**
* Auto-configuration of loadbalancer for Polaris.
@@ -52,42 +41,4 @@
@LoadBalancerClients(defaultConfiguration = PolarisLoadBalancerClientConfiguration.class)
public class PolarisLoadBalancerAutoConfiguration {
- @Bean
- public RestTemplateCustomizer polarisRestTemplateCustomizer(
- @Autowired(required = false) RetryLoadBalancerInterceptor retryLoadBalancerInterceptor,
- @Autowired(required = false) LoadBalancerInterceptor loadBalancerInterceptor) {
- return restTemplate -> {
- List list = new ArrayList<>(restTemplate.getInterceptors());
- // LoadBalancerInterceptor must invoke before EnhancedRestTemplateInterceptor
- int addIndex = list.size();
- if (CollectionUtils.containsInstance(list, retryLoadBalancerInterceptor) || CollectionUtils.containsInstance(list, loadBalancerInterceptor)) {
- ClientHttpRequestInterceptor enhancedRestTemplateInterceptor = null;
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) instanceof EnhancedRestTemplateInterceptor) {
- enhancedRestTemplateInterceptor = list.get(i);
- addIndex = i;
- }
- }
- if (enhancedRestTemplateInterceptor != null) {
- list.remove(addIndex);
- list.add(enhancedRestTemplateInterceptor);
- }
- }
- else {
- if (retryLoadBalancerInterceptor != null || loadBalancerInterceptor != null) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) instanceof EnhancedRestTemplateInterceptor) {
- addIndex = i;
- }
- }
- list.add(addIndex,
- retryLoadBalancerInterceptor != null
- ? retryLoadBalancerInterceptor
- : loadBalancerInterceptor);
- }
- }
- restTemplate.setInterceptors(list);
- };
- }
-
}
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java
index 8a0c471fd..9a0f99936 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java
@@ -57,7 +57,7 @@ public class PolarisRegistration implements Registration {
private final StaticMetadataManager staticMetadataManager;
- private final String serviceId;
+ private String serviceId;
private final String host;
private final boolean isSecure;
private final ServletWebServerApplicationContext servletWebServerApplicationContext;
@@ -164,6 +164,10 @@ public void customize() {
}
}
+ public void setServiceId(String serviceId) {
+ this.serviceId = serviceId;
+ }
+
@Override
public String getServiceId() {
return serviceId;
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java
index f98591bcd..71de4be33 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java
@@ -23,6 +23,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.common.util.OkHttpUtil;
import com.tencent.cloud.common.util.OtUtils;
@@ -106,6 +107,7 @@ public void register(PolarisRegistration registration) {
}
registration.customize();
String serviceId = registration.getServiceId();
+ MetadataContext.setLocalService(serviceId);
// Register instance.
InstanceRegisterRequest instanceRegisterRequest = new InstanceRegisterRequest();
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java
index f8e1c2e98..f9a6eb77a 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java
@@ -17,7 +17,10 @@
package com.tencent.cloud.plugin.lossless;
+import java.lang.reflect.Field;
+import java.net.URI;
import java.util.Collections;
+import java.util.Map;
import com.tencent.cloud.common.util.OkHttpUtil;
import com.tencent.cloud.plugin.lossless.config.LosslessAutoConfiguration;
@@ -28,6 +31,7 @@
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.cloud.polaris.registry.PolarisRegistration;
import com.tencent.cloud.polaris.registry.PolarisServiceRegistry;
+import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
@@ -40,6 +44,9 @@
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationUtils;
+import org.springframework.cloud.client.serviceregistry.Registration;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
@@ -106,6 +113,31 @@ public class LosslessRegistryAspectTest {
.withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
.withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx");
+ private final WebApplicationContextRunner contextRunner3 = new WebApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(
+ MockDiscoveryConfiguration.class,
+ LosslessAutoConfiguration.class,
+ LosslessPropertiesBootstrapConfiguration.class,
+ PolarisContextAutoConfiguration.class,
+ PolarisPropertiesConfiguration.class,
+ PolarisDiscoveryClientConfiguration.class,
+ PolarisDiscoveryAutoConfiguration.class))
+ .withPropertyValues("spring.cloud.nacos.discovery.enabled=false")
+ .withPropertyValues("spring.cloud.polaris.lossless.enabled=true")
+ .withPropertyValues("spring.cloud.polaris.lossless.healthCheckInterval=1000")
+ .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=/test")
+ .withPropertyValues("spring.cloud.polaris.admin.port=28082")
+ .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
+ .withPropertyValues("server.port=" + APPLICATION_PORT)
+ .withPropertyValues("spring.cloud.polaris.localIpAddress=" + HOST)
+ .withPropertyValues("spring.cloud.polaris.localPort=" + APPLICATION_PORT)
+ .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
+ .withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
+ .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx")
+ .withPropertyValues("spring.autoconfigure.exclude="
+ + "org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration");
+
+
@BeforeAll
static void beforeAll() throws Exception {
namingServer = NamingServer.startNamingServer(10081);
@@ -185,9 +217,121 @@ public void testRegister2() {
});
}
+ @Test
+ public void testRegister3() {
+ this.contextRunner3.run(context -> {
+
+ AbstractAutoServiceRegistration autoServiceRegistration = context.getBean(AbstractAutoServiceRegistration.class);
+
+ assertThatCode(() -> {
+ AutoServiceRegistrationUtils.register(autoServiceRegistration);
+ }).doesNotThrowAnyException();
+
+ Thread.sleep(2000);
+
+ assertThatCode(() -> {
+ AutoServiceRegistrationUtils.deRegister(autoServiceRegistration);
+ }).doesNotThrowAnyException();
+
+ LosslessRegistryAspect losslessRegistryAspect = context.getBean(LosslessRegistryAspect.class);
+ Field field = LosslessRegistryAspect.class.getDeclaredField("registrationTransformer");
+ field.setAccessible(true);
+ RegistrationTransformer registrationTransformer = (RegistrationTransformer) field.get(losslessRegistryAspect);
+ assertThat(registrationTransformer.getClass().getName().contains("PolarisRegistrationTransformer"));
+
+ field = LosslessRegistryAspect.class.getDeclaredField("registration");
+ field.setAccessible(true);
+ Registration registration = (Registration) field.get(losslessRegistryAspect);
+ assertThat(registration.getClass().getName().contains("PolarisRegistration"));
+
+ field = LosslessRegistryAspect.class.getDeclaredField("serviceRegistry");
+ field.setAccessible(true);
+ ServiceRegistry serviceRegistry = (ServiceRegistry) field.get(losslessRegistryAspect);
+ assertThat(serviceRegistry.getClass().getName().contains("PolarisServiceRegistry"));
+ });
+ }
+
@Configuration
@EnableAutoConfiguration
static class PolarisPropertiesConfiguration {
}
+
+ @Configuration
+ static class MockDiscoveryConfiguration {
+ @Bean
+ public ServiceRegistry mockServiceRegistry() {
+ return new ServiceRegistry() {
+ @Override
+ public void register(Registration registration) {
+
+ }
+
+ @Override
+ public void deregister(Registration registration) {
+
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void setStatus(Registration registration, String status) {
+
+ }
+
+ @Override
+ public Object getStatus(Registration registration) {
+ return null;
+ }
+ };
+ }
+
+ @Bean
+ public Registration mockRegistration() {
+ return new Registration() {
+ @Override
+ public String getServiceId() {
+ return null;
+ }
+
+ @Override
+ public String getHost() {
+ return null;
+ }
+
+ @Override
+ public int getPort() {
+ return 0;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return false;
+ }
+
+ @Override
+ public URI getUri() {
+ return null;
+ }
+
+ @Override
+ public Map getMetadata() {
+ return null;
+ }
+ };
+ }
+
+ @Bean
+ public RegistrationTransformer mockRegistrationTransformer() {
+ return new RegistrationTransformer() {
+ @Override
+ public String getRegistry() {
+ return null;
+ }
+ };
+ }
+ }
}
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java
index 02a77ed76..07a13e6e2 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java
@@ -104,6 +104,7 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, path);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
+ LOG.info("block by ratelimit rule, uri:{}", exchange.getRequest().getURI());
ServerHttpResponse response = exchange.getResponse();
DataBuffer dataBuffer;
if (Objects.nonNull(quotaResponse.getActiveRule())
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java
index a4a312dc4..70c68dbda 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java
@@ -99,6 +99,7 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht
try {
quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, request.getRequestURI());
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
+ LOG.info("block by ratelimit rule, uri:{}", request.getRequestURI());
if (Objects.nonNull(quotaResponse.getActiveRule())
&& StringUtils.isNotBlank(quotaResponse.getActiveRule().getCustomResponse().getBody())) {
response.setStatus(polarisRateLimitProperties.getRejectHttpCode());
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java
index 48857ad46..370cbf519 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java
@@ -31,7 +31,6 @@
import com.tencent.cloud.polaris.router.interceptor.NamespaceRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.NearbyRouterRequestInterceptor;
import com.tencent.cloud.polaris.router.interceptor.RuleBasedRouterRequestInterceptor;
-import com.tencent.cloud.rpc.enhancement.instrument.resttemplate.EnhancedRestTemplateInterceptor;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
@@ -112,13 +111,7 @@ public RouterLabelRestTemplateInterceptor routerLabelRestTemplateInterceptor() {
public SmartInitializingSingleton addRouterLabelInterceptorForRestTemplate(RouterLabelRestTemplateInterceptor interceptor) {
return () -> restTemplates.forEach(restTemplate -> {
List list = new ArrayList<>(restTemplate.getInterceptors());
- int addIndex = list.size();
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i) instanceof EnhancedRestTemplateInterceptor) {
- addIndex = i;
- }
- }
- list.add(addIndex, interceptor);
+ list.add(interceptor);
restTemplate.setInterceptors(list);
});
}
diff --git a/spring-cloud-tencent-commons/pom.xml b/spring-cloud-tencent-commons/pom.xml
index 59daa7efe..3c103e3ac 100644
--- a/spring-cloud-tencent-commons/pom.xml
+++ b/spring-cloud-tencent-commons/pom.xml
@@ -15,7 +15,7 @@
2.6
- 2.11.0
+ 2.14.0
@@ -53,11 +53,23 @@
org.springframework.boot
spring-boot-starter-validation
+
+
+ tomcat-embed-el
+ org.apache.tomcat.embed
+
+
org.springframework.cloud
spring-cloud-starter
+
+
+ spring-security-rsa
+ org.springframework.security
+
+
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java
index 2de8a2c75..ee32161b0 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java
@@ -43,4 +43,23 @@ public final class ContextConstant {
private ContextConstant() {
}
+
+ public static final class CircuitBreaker {
+ /**
+ * polaris circuit breaker.
+ */
+ public static final String POLARIS_CIRCUIT_BREAKER = "PolarisCircuitBreaker";
+ /**
+ * circuit breaker start time.
+ */
+ public static final String CIRCUIT_BREAKER_START_TIME = "CIRCUIT_BREAKER_START_TIME";
+ /**
+ * circuit breaker fallback http response.
+ */
+ public static final String CIRCUIT_BREAKER_FALLBACK_HTTP_RESPONSE = "CIRCUIT_BREAKER_FALLBACK_HTTP_RESPONSE";
+
+ private CircuitBreaker() {
+
+ }
+ }
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
index e01e41afb..733d69057 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
@@ -319,4 +319,8 @@ public void putFragmentContext(String fragment, Map context) {
break;
}
}
+
+ public static void setLocalService(String service) {
+ LOCAL_SERVICE = service;
+ }
}
diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml
index 8d04a1816..da6c8c3fe 100644
--- a/spring-cloud-tencent-dependencies/pom.xml
+++ b/spring-cloud-tencent-dependencies/pom.xml
@@ -74,18 +74,20 @@
2.0.1.0-2021.0.9-SNAPSHOT
- 2.0.0.0
+ 2.0.0.1
- 32.0.1-jre
- 1.7.0
- 4.9.0
+ 1.78.1
1.12.19
- 2.13.5
- 3.21.7
- 3.9.0
+ 1.17.1
+ 32.1.3-jre
+ 2.15.3
+ 4.9.0
2.9.9
+ 3.21.7
2.0.2
+ 2.0
+ 1.7.0
3.3.0
@@ -220,6 +222,12 @@
${revision}
+
+ com.tencent.cloud
+ spring-cloud-tencent-security-protection-plugin
+ ${revision}
+
+
com.google.guava
@@ -265,18 +273,36 @@
${byte-buddy.version}
-
- com.squareup.okio
- okio
- ${okio.version}
-
-
joda-time
joda-time
${joda-time.version}
+
+ org.bouncycastle
+ bcpkix-jdk18on
+ ${bcpkix-jdk18on.version}
+
+
+
+ org.bouncycastle
+ bcprov-jdk15to18
+ ${bcpkix-jdk18on.version}
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+
+ commons-codec
+ commons-codec
+ ${commons-codec.version}
+
+
org.mockito
mockito-inline
diff --git a/spring-cloud-tencent-plugin-starters/pom.xml b/spring-cloud-tencent-plugin-starters/pom.xml
index b80d06b7f..4159c7843 100644
--- a/spring-cloud-tencent-plugin-starters/pom.xml
+++ b/spring-cloud-tencent-plugin-starters/pom.xml
@@ -22,6 +22,7 @@
spring-cloud-starter-tencent-threadlocal-plugin
spring-cloud-starter-tencent-trace-plugin
spring-cloud-starter-tencent-fault-tolerance
+ spring-cloud-tencent-security-protection-plugin
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java
index 7bcfeeab1..bdd454c91 100644
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java
@@ -49,7 +49,6 @@ public InstanceTransformer instanceTransformer() {
}
@Bean
- @ConditionalOnMissingBean
@ConditionalOnClass(name = "com.alibaba.cloud.nacos.registry.NacosRegistration")
public RegistrationTransformer registrationTransformer() {
return new NacosRegistrationTransformer();
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java
index 4c902dd2a..37e4272d1 100644
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java
@@ -17,6 +17,8 @@
package com.tencent.cloud.plugin.lossless.config;
+import java.util.List;
+
import com.tencent.cloud.plugin.lossless.LosslessRegistryAspect;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
@@ -43,11 +45,36 @@ public class LosslessAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LosslessRegistryAspect losslessRegistryAspect(
- ServiceRegistry serviceRegistry, Registration registration, PolarisContextProperties properties,
- LosslessProperties losslessProperties, PolarisSDKContextManager polarisSDKContextManager,
- RegistrationTransformer registrationTransformer) {
- return new LosslessRegistryAspect(serviceRegistry, registration, properties, losslessProperties,
- polarisSDKContextManager, registrationTransformer);
+ List serviceRegistryList, List registrationList, List registrationTransformerList,
+ PolarisContextProperties properties, LosslessProperties losslessProperties, PolarisSDKContextManager polarisSDKContextManager) {
+
+ ServiceRegistry targetServiceRegistry = serviceRegistryList.size() > 0 ? serviceRegistryList.get(0) : null;
+ Registration targetRegistration = registrationList.size() > 0 ? registrationList.get(0) : null;
+ RegistrationTransformer targetRegistrationTransformer = registrationTransformerList.size() > 0 ? registrationTransformerList.get(0) : null;
+ // if contains multiple service registry, find the polaris service registr
+ if (serviceRegistryList.size() > 1) {
+ for (ServiceRegistry serviceRegistry : serviceRegistryList) {
+ if (serviceRegistry.getClass().getName().contains("PolarisServiceRegistry")) {
+ targetServiceRegistry = serviceRegistry;
+ }
+ }
+ }
+ if (registrationList.size() > 1) {
+ for (Registration registration : registrationList) {
+ if (registration.getClass().getName().contains("PolarisRegistration")) {
+ targetRegistration = registration;
+ }
+ }
+ }
+ if (registrationTransformerList.size() > 1) {
+ for (RegistrationTransformer registrationTransformer : registrationTransformerList) {
+ if (registrationTransformer.getClass().getName().contains("PolarisRegistrationTransformer")) {
+ targetRegistrationTransformer = registrationTransformer;
+ }
+ }
+ }
+ return new LosslessRegistryAspect(targetServiceRegistry, targetRegistration, properties, losslessProperties,
+ polarisSDKContextManager, targetRegistrationTransformer);
}
}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/pom.xml
new file mode 100644
index 000000000..1674db4e8
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/pom.xml
@@ -0,0 +1,59 @@
+
+
+
+ spring-cloud-tencent-plugin-starters
+ com.tencent.cloud
+ ${revision}
+ ../pom.xml
+
+ 4.0.0
+
+ spring-cloud-tencent-security-protection-plugin
+ Spring Cloud Tencent Lossless Plugin
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+
+ org.springframework
+ spring-beans
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ org.springframework
+ spring-webmvc
+ true
+
+
+
+ org.springframework
+ spring-webflux
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.mockito
+ mockito-inline
+ test
+
+
+
+
+
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/java/com/tencent/cloud/plugin/protection/ExitUtils.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/java/com/tencent/cloud/plugin/protection/ExitUtils.java
new file mode 100644
index 000000000..2bf12db99
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/java/com/tencent/cloud/plugin/protection/ExitUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.protection;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * ExitUtils.
+ *
+ * @author Shedfree Wu
+ */
+public final class ExitUtils {
+
+ private ExitUtils() {
+
+ }
+
+ public static void exit(ApplicationContext context) {
+ exit(context, 3000);
+ }
+
+ public static void exit(ApplicationContext context, int delay) {
+ if (context instanceof ConfigurableApplicationContext) {
+ ConfigurableApplicationContext configurableContext = (ConfigurableApplicationContext) context;
+ configurableContext.close();
+ }
+
+ try {
+ Thread.sleep(delay);
+ }
+ catch (InterruptedException e) {
+ // do nothing
+ }
+ System.exit(0);
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/java/com/tencent/cloud/plugin/protection/SecurityProtectionAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/java/com/tencent/cloud/plugin/protection/SecurityProtectionAutoConfiguration.java
new file mode 100644
index 000000000..589240c63
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/java/com/tencent/cloud/plugin/protection/SecurityProtectionAutoConfiguration.java
@@ -0,0 +1,87 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.protection;
+
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.function.RouterFunction;
+
+/**
+ * SecurityProtectionAutoConfiguration.
+ *
+ * @author Shedfree Wu
+ */
+@Configuration
+public class SecurityProtectionAutoConfiguration {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SecurityProtectionAutoConfiguration.class);
+
+ @Configuration
+ @ConditionalOnProperty(name = "spring.cloud.tencent.security.protection.servlet.enabled", matchIfMissing = true)
+ @ConditionalOnClass(name = {"org.springframework.web.servlet.function.RouterFunction"})
+ static class ServletProtectionConfiguration implements InitializingBean {
+
+ @Autowired(required = false)
+ List routerFunctions;
+
+ @Autowired
+ ApplicationContext applicationContext;
+
+ @Override
+ public void afterPropertiesSet() {
+ if (routerFunctions != null && !routerFunctions.isEmpty()) {
+ LOGGER.error("Detected the presence of webmvc RouterFunction-related beans, which may trigger the CVE-2024-38819 vulnerability. The program will soon exit.");
+ LOGGER.error("routerFunctions:{}: ", routerFunctions);
+
+ ExitUtils.exit(applicationContext);
+ }
+ }
+ }
+
+ @Configuration
+ @ConditionalOnProperty(name = "spring.cloud.tencent.security.protection.reactive.enabled", matchIfMissing = true)
+ @ConditionalOnClass(name = {"org.springframework.web.reactive.function.server.RouterFunction"})
+ static class ReactiveProtectionConfiguration implements InitializingBean {
+
+ @Autowired(required = false)
+ List routerFunctions;
+
+ @Autowired
+ ApplicationContext applicationContext;
+
+ @Override
+ public void afterPropertiesSet() {
+ if (routerFunctions != null && !routerFunctions.isEmpty()) {
+ LOGGER.error("Detected the presence of webflux RouterFunction-related beans, which may trigger the CVE-2024-38819 vulnerability. The program will soon exit.");
+ LOGGER.error("routerFunctions:{}: ", routerFunctions);
+ ExitUtils.exit(applicationContext);
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..3e0f0ee08
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+# Auto Configuration
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.tencent.cloud.plugin.protection.SecurityProtectionAutoConfiguration
\ No newline at end of file
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/test/java/com/tencent/cloud/plugin/protection/SecurityProtectionAutoConfigurationTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/test/java/com/tencent/cloud/plugin/protection/SecurityProtectionAutoConfigurationTest.java
new file mode 100644
index 000000000..cb93c89ea
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-security-protection-plugin/src/test/java/com/tencent/cloud/plugin/protection/SecurityProtectionAutoConfigurationTest.java
@@ -0,0 +1,189 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.protection;
+
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.web.servlet.function.RouterFunction;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link SecurityProtectionAutoConfiguration}.
+ */
+@ExtendWith(MockitoExtension.class)
+class SecurityProtectionAutoConfigurationTest {
+
+ @Mock
+ private ConfigurableApplicationContext applicationContext;
+
+ @Test
+ void testServletProtectionNoRouterFunctions() {
+ // Arrange
+ SecurityProtectionAutoConfiguration.ServletProtectionConfiguration config =
+ new SecurityProtectionAutoConfiguration.ServletProtectionConfiguration();
+ config.applicationContext = applicationContext;
+ config.routerFunctions = null;
+
+ // Act
+ config.afterPropertiesSet();
+
+ // Verify
+ // Should not call exit when no RouterFunctions present
+ verify(applicationContext, never()).close();
+ }
+
+ @Test
+ void testServletProtectionEmptyRouterFunctions() {
+ // Arrange
+ SecurityProtectionAutoConfiguration.ServletProtectionConfiguration config =
+ new SecurityProtectionAutoConfiguration.ServletProtectionConfiguration();
+ config.applicationContext = applicationContext;
+ config.routerFunctions = new ArrayList<>();
+
+ // Act
+ config.afterPropertiesSet();
+
+ // Verify
+ // Should not call exit when RouterFunctions list is empty
+ verify(applicationContext, never()).close();
+ }
+
+ @Test
+ void testServletProtectionWithRouterFunctions() {
+ // Arrange
+ SecurityProtectionAutoConfiguration.ServletProtectionConfiguration config =
+ new SecurityProtectionAutoConfiguration.ServletProtectionConfiguration();
+ config.applicationContext = mock(ConfigurableApplicationContext.class);
+ List routerFunctions = new ArrayList<>();
+ routerFunctions.add(mock(RouterFunction.class));
+ config.routerFunctions = routerFunctions;
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ config.afterPropertiesSet();
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ @Test
+ void testReactiveProtectionNoRouterFunctions() {
+ // Arrange
+ SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration config =
+ new SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration();
+ config.applicationContext = applicationContext;
+ config.routerFunctions = null;
+
+ // Act
+ config.afterPropertiesSet();
+
+ // Verify
+ verify(applicationContext, never()).close();
+ }
+
+ @Test
+ void testReactiveProtectionEmptyRouterFunctions() {
+ // Arrange
+ SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration config =
+ new SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration();
+ config.applicationContext = applicationContext;
+ config.routerFunctions = new ArrayList<>();
+
+ // Act
+ config.afterPropertiesSet();
+
+ // Verify
+ verify(applicationContext, never()).close();
+ }
+
+ @Test
+ void testReactiveProtectionWithRouterFunctions() {
+ // Arrange
+ SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration config =
+ new SecurityProtectionAutoConfiguration.ReactiveProtectionConfiguration();
+ config.applicationContext = mock(ConfigurableApplicationContext.class);
+ List routerFunctions = new ArrayList<>();
+ routerFunctions.add(mock(org.springframework.web.reactive.function.server.RouterFunction.class));
+ config.routerFunctions = routerFunctions;
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ config.afterPropertiesSet();
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ @Test
+ void testInterruptedExceptionHandling() throws InterruptedException {
+ // Arrange
+ ConfigurableApplicationContext mockContext = mock(ConfigurableApplicationContext.class);
+ Thread testThread = new Thread(() -> ExitUtils.exit(mockContext, 3000));
+
+
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ // Act
+ testThread.start();
+ testThread.interrupt();
+ Thread.sleep(6000);
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ public static class ExitSecurityManager extends SecurityManager {
+
+ @Override
+ public void checkPermission(Permission perm) {
+ if (perm.getName().contains("exitVM")) {
+ throw new SecurityException("System.exit is not allowed");
+ }
+ }
+ }
+}
diff --git a/spring-cloud-tencent-polaris-context/pom.xml b/spring-cloud-tencent-polaris-context/pom.xml
index 491ef31b9..4d43b7008 100644
--- a/spring-cloud-tencent-polaris-context/pom.xml
+++ b/spring-cloud-tencent-polaris-context/pom.xml
@@ -174,6 +174,16 @@
com.tencent.polaris
registry-memory
+
+
+ error_prone_annotations
+ com.google.errorprone
+
+
+ j2objc-annotations
+ com.google.j2objc
+
+
diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java
new file mode 100644
index 000000000..d756714e4
--- /dev/null
+++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/CircuitBreakerStatusCodeException.java
@@ -0,0 +1,34 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.context;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.client.HttpStatusCodeException;
+
+/**
+ * CircuitBreaker HttpStatusCodeException.
+ *
+ * @author Shedfree Wu
+ */
+public class CircuitBreakerStatusCodeException extends HttpStatusCodeException {
+
+ public CircuitBreakerStatusCodeException(HttpStatus statusCode) {
+ super(statusCode);
+ }
+
+}
diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/listener/FailedEventApplicationListener.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/listener/FailedEventApplicationListener.java
new file mode 100644
index 000000000..b963141e3
--- /dev/null
+++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/listener/FailedEventApplicationListener.java
@@ -0,0 +1,69 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.context.listener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.boot.context.event.ApplicationFailedEvent;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Failed event listener.
+ *
+ * @author skyehtzhang
+ */
+@Configuration
+public class FailedEventApplicationListener implements ApplicationListener, ApplicationContextAware {
+
+ private static final Logger logger = LoggerFactory.getLogger(FailedEventApplicationListener.class);
+
+ private ApplicationContext applicationContext;
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent event) {
+ if (event instanceof ApplicationFailedEvent) {
+ ApplicationFailedEvent failedEvent = (ApplicationFailedEvent) event;
+ if (failedEvent.getException() != null) {
+ logger.error("[onApplicationEvent] exception in failed event", failedEvent.getException());
+ }
+
+ if (applicationContext instanceof ConfigurableApplicationContext) {
+ ((ConfigurableApplicationContext) applicationContext).close();
+ }
+ try {
+ Thread.sleep(3000);
+ }
+ catch (InterruptedException e) {
+ // do nothing
+ }
+ System.exit(0);
+ }
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+}
diff --git a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories
index ac093c297..7751f48cc 100644
--- a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories
@@ -6,7 +6,8 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration,\
com.tencent.cloud.polaris.context.config.extend.tsf.TsfContextBootstrapConfiguration
org.springframework.context.ApplicationListener=\
- com.tencent.cloud.polaris.context.logging.PolarisLoggingApplicationListener
+ com.tencent.cloud.polaris.context.logging.PolarisLoggingApplicationListener,\
+ com.tencent.cloud.polaris.context.listener.FailedEventApplicationListener
org.springframework.boot.env.EnvironmentPostProcessor=\
com.tencent.cloud.polaris.context.config.PolarisContextEnvironmentPostProcessor,\
com.tencent.cloud.polaris.context.config.extend.tsf.TsfCoreEnvironmentPostProcessor
diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/listener/FailedEventApplicationListenerTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/listener/FailedEventApplicationListenerTest.java
new file mode 100644
index 000000000..8e38f769f
--- /dev/null
+++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/listener/FailedEventApplicationListenerTest.java
@@ -0,0 +1,154 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.context.listener;
+
+import java.security.Permission;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.boot.context.event.ApplicationFailedEvent;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import static org.mockito.Mockito.when;
+
+
+class FailedEventApplicationListenerTest {
+
+ @Mock
+ private ConfigurableApplicationContext mockConfigurableContext;
+
+ @Mock
+ private ApplicationContext mockApplicationContext;
+
+ @Mock
+ private ApplicationFailedEvent mockFailedEvent;
+
+ private FailedEventApplicationListener listener;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ listener = new FailedEventApplicationListener();
+ }
+
+ @Test
+ void testSetApplicationContext() {
+ // Test setting application context
+ listener.setApplicationContext(mockApplicationContext);
+ }
+
+ @Test
+ void testOnApplicationEventWithConfigurableContext() {
+ // Arrange
+ listener.setApplicationContext(mockConfigurableContext);
+ when(mockFailedEvent.getException()).thenReturn(new RuntimeException("Test Exception"));
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ // Act
+ listener.onApplicationEvent(mockFailedEvent);
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ @Test
+ void testOnApplicationEventWithNonConfigurableContext() {
+ // Arrange
+ listener.setApplicationContext(mockApplicationContext);
+ when(mockFailedEvent.getException()).thenReturn(new RuntimeException("Test Exception"));
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ // Act
+ listener.onApplicationEvent(mockFailedEvent);
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ @Test
+ void testOnApplicationEventWithInterruptedException() {
+ // Arrange
+ listener.setApplicationContext(mockConfigurableContext);
+ Thread.currentThread().interrupt(); // Simulate interruption
+
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ // Act
+ listener.onApplicationEvent(mockFailedEvent);
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ @Test
+ void testOnApplicationEventWithNullException() {
+ // Arrange
+ listener.setApplicationContext(mockConfigurableContext);
+ when(mockFailedEvent.getException()).thenReturn(null);
+
+
+ SecurityManager originalSecurityManager = System.getSecurityManager();
+ System.setSecurityManager(new ExitSecurityManager());
+
+ try {
+ // Act
+ listener.onApplicationEvent(mockFailedEvent);
+ }
+ catch (SecurityException e) {
+ // Ignore
+ }
+ finally {
+ System.setSecurityManager(originalSecurityManager);
+ }
+ }
+
+ public static class ExitSecurityManager extends SecurityManager {
+
+ @Override
+ public void checkPermission(Permission perm) {
+ if (perm.getName().contains("exitVM")) {
+ throw new SecurityException("System.exit is not allowed");
+ }
+ }
+ }
+}
diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/logging/PolarisLoggingPathSystemPropertyTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/logging/PolarisLoggingPathSystemPropertyTest.java
index a7539e6f7..44069f621 100644
--- a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/logging/PolarisLoggingPathSystemPropertyTest.java
+++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/logging/PolarisLoggingPathSystemPropertyTest.java
@@ -28,7 +28,7 @@
import static org.assertj.core.api.Assertions.assertThat;
/**
- * Test for {@link PolarisLoggingApplicationListener}
+ * Test for {@link PolarisLoggingApplicationListener}.
*
* @author wenxuan70
*/
diff --git a/spring-cloud-tencent-rpc-enhancement/pom.xml b/spring-cloud-tencent-rpc-enhancement/pom.xml
index 4cc4ab831..b21f18c56 100644
--- a/spring-cloud-tencent-rpc-enhancement/pom.xml
+++ b/spring-cloud-tencent-rpc-enhancement/pom.xml
@@ -37,6 +37,12 @@
org.springframework.boot
spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
@@ -80,6 +86,12 @@
test
+
+ io.projectreactor
+ reactor-test
+ test
+
+
org.mockito
mockito-inline
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java
index 52ad1d56f..7b4c7013f 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java
@@ -27,7 +27,6 @@
import com.tencent.cloud.rpc.enhancement.instrument.feign.EnhancedLoadBalancerClientAspect;
import com.tencent.cloud.rpc.enhancement.instrument.filter.EnhancedReactiveFilter;
import com.tencent.cloud.rpc.enhancement.instrument.filter.EnhancedServletFilter;
-import com.tencent.cloud.rpc.enhancement.instrument.resttemplate.EnhancedRestTemplateInterceptor;
import com.tencent.cloud.rpc.enhancement.instrument.resttemplate.PolarisLoadBalancerRequestTransformer;
import com.tencent.cloud.rpc.enhancement.instrument.scg.EnhancedGatewayGlobalFilter;
import com.tencent.cloud.rpc.enhancement.instrument.webclient.EnhancedWebClientExchangeFilterFunction;
@@ -89,7 +88,6 @@ public InstanceTransformer instanceTransformer() {
}
@Bean
- @ConditionalOnMissingBean
@ConditionalOnClass(name = "com.tencent.cloud.polaris.registry.PolarisRegistration")
public RegistrationTransformer registrationTransformer() {
return new PolarisRegistrationTransformer();
@@ -99,9 +97,9 @@ public RegistrationTransformer registrationTransformer() {
@Lazy
public EnhancedPluginRunner enhancedFeignPluginRunner(
@Autowired(required = false) List enhancedPlugins,
- @Autowired(required = false) Registration registration,
+ @Autowired(required = false) List registrations,
PolarisSDKContextManager polarisSDKContextManager) {
- return new DefaultEnhancedPluginRunner(enhancedPlugins, registration, polarisSDKContextManager.getSDKContext());
+ return new DefaultEnhancedPluginRunner(enhancedPlugins, registrations, polarisSDKContextManager.getSDKContext());
}
@Bean
@@ -183,23 +181,6 @@ public EnhancedLoadBalancerClientAspect enhancedLoadBalancerClientAspect() {
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
protected static class PolarisRestTemplateAutoConfiguration {
- @Autowired(required = false)
- private List restTemplates = Collections.emptyList();
-
- @Bean
- public EnhancedRestTemplateInterceptor enhancedPolarisRestTemplateReporter(@Lazy EnhancedPluginRunner pluginRunner) {
- return new EnhancedRestTemplateInterceptor(pluginRunner);
- }
-
- @Bean
- public SmartInitializingSingleton setPolarisReporterForRestTemplate(EnhancedRestTemplateInterceptor reporter) {
- return () -> {
- for (RestTemplate restTemplate : restTemplates) {
- restTemplate.getInterceptors().add(reporter);
- }
- };
- }
-
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = {"org.springframework.cloud.client.loadbalancer.LoadBalancerRequestTransformer"})
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java
index e4c2d1a5b..ad60f858d 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java
@@ -19,16 +19,24 @@
import java.io.IOException;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext;
+import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
+import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
import feign.Client;
import feign.Request;
import feign.Request.Options;
+import feign.RequestTemplate;
import feign.Response;
import org.springframework.cloud.client.DefaultServiceInstance;
@@ -61,16 +69,19 @@ public Response execute(Request request, Options options) throws IOException {
request.headers().forEach((s, strings) -> requestHeaders.addAll(s, new ArrayList<>(strings)));
URI url = URI.create(request.url());
+ URI serviceUrl = url.resolve(request.requestTemplate().url());
+
EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder()
.httpHeaders(requestHeaders)
.httpMethod(HttpMethod.valueOf(request.httpMethod().name()))
.url(url)
+ .serviceUrl(serviceUrl)
.build();
enhancedPluginContext.setRequest(enhancedRequestContext);
enhancedPluginContext.setOriginRequest(request);
enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
- String svcName = request.requestTemplate().feignTarget().name();
+ String svcName = serviceUrl.getHost();
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(
String.format("%s-%s-%d", svcName, url.getHost(), url.getPort()),
svcName, url.getHost(), url.getPort(), url.getScheme().equals("https"));
@@ -82,11 +93,11 @@ public Response execute(Request request, Options options) throws IOException {
enhancedPluginContext.setTargetServiceInstance(serviceInstance, url);
}
- // Run pre enhanced plugins.
- pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
-
long startMillis = System.currentTimeMillis();
try {
+ // Run pre enhanced plugins.
+ pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
+
Response response = delegate.execute(request, options);
enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis);
@@ -103,6 +114,15 @@ public Response execute(Request request, Options options) throws IOException {
pluginRunner.run(EnhancedPluginType.Client.POST, enhancedPluginContext);
return response;
}
+ catch (CallAbortedException callAbortedException) {
+ // circuit breaker fallback, not need to run post/exception enhanced plugins.
+ if (callAbortedException.getFallbackInfo() != null) {
+ return getFallbackResponse(callAbortedException.getFallbackInfo());
+ }
+ else {
+ throw callAbortedException;
+ }
+ }
catch (IOException origin) {
enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis);
enhancedPluginContext.setThrowable(origin);
@@ -115,4 +135,25 @@ public Response execute(Request request, Options options) throws IOException {
pluginRunner.run(EnhancedPluginType.Client.FINALLY, enhancedPluginContext);
}
}
+
+ private Response getFallbackResponse(CircuitBreakerStatus.FallbackInfo fallbackInfo) {
+
+ Response.Builder responseBuilder = Response.builder()
+ .status(fallbackInfo.getCode());
+ if (fallbackInfo.getHeaders() != null) {
+ Map> headers = new HashMap<>();
+ fallbackInfo.getHeaders().forEach((k, v) -> headers.put(k, Collections.singleton(v)));
+ responseBuilder.headers(headers);
+ }
+ if (fallbackInfo.getBody() != null) {
+ responseBuilder.body(fallbackInfo.getBody(), StandardCharsets.UTF_8);
+ }
+ // Feign Response need a nonnull Request,
+ // which is not important in fallback response (no real request),
+ // so we create a fake one
+ Request fakeRequest = Request.create(Request.HttpMethod.GET, "/", new HashMap<>(), Request.Body.empty(), new RequestTemplate());
+ responseBuilder.request(fakeRequest);
+
+ return responseBuilder.build();
+ }
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateInterceptor.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java
similarity index 58%
rename from spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateInterceptor.java
rename to spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java
index 5f739118c..b30c7835a 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateInterceptor.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java
@@ -18,18 +18,25 @@
package com.tencent.cloud.rpc.enhancement.instrument.resttemplate;
import java.io.IOException;
+import java.net.URI;
+import java.util.Optional;
+import com.tencent.cloud.common.constant.ContextConstant;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext;
+import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
+import com.tencent.polaris.metadata.core.MetadataObjectValue;
+import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
+import org.springframework.cloud.client.loadbalancer.ServiceRequestWrapper;
import org.springframework.http.HttpRequest;
-import org.springframework.http.client.ClientHttpRequestExecution;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import static com.tencent.cloud.rpc.enhancement.instrument.resttemplate.PolarisLoadBalancerRequestTransformer.LOAD_BALANCER_SERVICE_INSTANCE;
@@ -39,37 +46,67 @@
*
* @author sean yu
*/
-public class EnhancedRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+public class EnhancedRestTemplateWrapInterceptor {
private final EnhancedPluginRunner pluginRunner;
- public EnhancedRestTemplateInterceptor(EnhancedPluginRunner pluginRunner) {
+ private final LoadBalancerClient delegate;
+
+ public EnhancedRestTemplateWrapInterceptor(EnhancedPluginRunner pluginRunner, LoadBalancerClient delegate) {
this.pluginRunner = pluginRunner;
+ this.delegate = delegate;
}
- @Override
- public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
+
+ public ClientHttpResponse intercept(HttpRequest request, String serviceId,
+ LoadBalancerRequest loadBalancerRequest) throws IOException {
EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext();
+ URI serviceUrl = request.getURI();
+ if (request instanceof ServiceRequestWrapper) {
+ serviceUrl = ((ServiceRequestWrapper) request).getRequest().getURI();
+ }
+
EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder()
.httpHeaders(request.getHeaders())
.httpMethod(request.getMethod())
.url(request.getURI())
+ .serviceUrl(serviceUrl)
.build();
enhancedPluginContext.setRequest(enhancedRequestContext);
enhancedPluginContext.setOriginRequest(request);
enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
- enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get()
- .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), request.getURI());
+
// Run pre enhanced plugins.
- pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
+ try {
+ pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
+ }
+ catch (CallAbortedException callAbortedException) {
+ MetadataObjectValue