diff --git a/.github/workflows/junit_test.yml b/.github/workflows/junit_test.yml
index 35fed41eb..51e9cce15 100644
--- a/.github/workflows/junit_test.yml
+++ b/.github/workflows/junit_test.yml
@@ -5,24 +5,47 @@ name: Test with Junit
on:
push:
- branches: [ 2021.0 ]
+ branches:
+ - main
+ - 2021.0
+ - 2020.0
+ - greenwich
pull_request:
- branches: [ 2021.0 ]
+ branches:
+ - main
+ - 2021.0
+ - 2020.0
+ - greenwich
jobs:
build:
+ strategy:
+ matrix:
+ java: [ 8, 11, 17 ]
+ os: [ 'windows-latest', 'macos-latest', 'ubuntu-latest' ]
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
steps:
- name: Checkout codes
- uses: actions/checkout@v2
- - name: Set up JDK 8
- uses: actions/setup-java@v2
+ uses: actions/checkout@v3
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v3
with:
- java-version: '8'
- distribution: 'adopt'
+ distribution: 'temurin'
+ java-version: ${{ matrix.java }}
+ - name: Cache local Maven repository
+ uses: actions/cache@v3
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
# - name: Build with Maven
# run: mvn -B package --file pom.xml
- name: Test with Maven
run: mvn -B test --file pom.xml
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v3
+ with:
+ file: '**/target/site/jacoco/jacoco.xml'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4a65e9b3..425d0da06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,4 +4,10 @@
- [Upgrade: fix third-party lib CVEs & upgrade core spring libs version](https://github.com/Tencent/spring-cloud-tencent/pull/263)
- [feat:support reading configuration from application.yml or application.properties.](https://github.com/Tencent/spring-cloud-tencent/pull/262)
- [fix:fix ClassNotFoundException while not importing openfeign when using circuit-breaker module.](https://github.com/Tencent/spring-cloud-tencent/pull/271)
-- [fix:solve the chaos code problem on rejectTips](https://github.com/Tencent/spring-cloud-tencent/pull/283)
+- [fix:solve ratelimit-callee-service UnknownHostException.](https://github.com/Tencent/spring-cloud-tencent/pull/292)
+- [Feature: Add config change listener feature support](https://github.com/Tencent/spring-cloud-tencent/pull/299)
+- [Feature: Add config module unit test](https://github.com/Tencent/spring-cloud-tencent/pull/301)
+- [Feature:add restTemplate Report Polaris](https://github.com/Tencent/spring-cloud-tencent/pull/304)
+- [Update GitHub Actions workflow](https://github.com/Tencent/spring-cloud-tencent/pull/305)
+- [fix: 将blocking call改为non-blocking call](https://github.com/Tencent/spring-cloud-tencent/pull/309)
+- [fix:solve the chaos code problem on rejectTips](https://github.com/Tencent/spring-cloud-tencent/pull/283)
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b16ae8082..2561f4430 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,9 +38,11 @@
- spring-cloud-tencent-polaris-contextspring-cloud-tencent-commons
+ spring-cloud-tencent-polaris-context
+ spring-cloud-tencent-polaris-loadbalancerspring-cloud-starter-tencent-metadata-transfer
+ spring-cloud-starter-tencent-polaris-configspring-cloud-starter-tencent-polaris-discoveryspring-cloud-starter-tencent-polaris-ratelimitspring-cloud-starter-tencent-polaris-circuitbreaker
@@ -48,8 +50,6 @@
spring-cloud-tencent-dependenciesspring-cloud-tencent-examplesspring-cloud-tencent-coverage
- spring-cloud-starter-tencent-polaris-config
- spring-cloud-tencent-polaris-loadbalancer
@@ -95,7 +95,7 @@
5.3.21
- 0.8.3
+ 0.8.83.2.01.2.73.0.1
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java
index 2ad7fdc66..c4a7338df 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataFeignInterceptorTest.java
@@ -19,9 +19,10 @@
package com.tencent.cloud.metadata.core.intercepter;
import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor;
import feign.RequestInterceptor;
@@ -61,11 +62,13 @@ public class EncodeTransferMedataFeignInterceptorTest {
private TestApplication.TestFeign testFeign;
@Test
- public void test1() {
+ public void testTransitiveMetadataFromApplicationConfig() {
String metadata = testFeign.test();
- Assertions.assertThat(metadata).isEqualTo("{\"b\":\"2\"}");
- Assertions.assertThat(metadataLocalProperties.getContent().get("a")).isEqualTo("1");
- Assertions.assertThat(metadataLocalProperties.getContent().get("b")).isEqualTo("2");
+ Assertions.assertThat(metadata).isEqualTo("2");
+ Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
+ .isEqualTo("1");
+ Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
+ .isEqualTo("2");
}
@SpringBootApplication
@@ -77,16 +80,13 @@ public class EncodeTransferMedataFeignInterceptorTest {
public String test(
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr)
throws UnsupportedEncodingException {
- return URLDecoder.decode(customMetadataStr, "UTF-8");
+ return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
}
@FeignClient(name = "test-feign", url = "http://localhost:8081")
public interface TestFeign {
- @RequestMapping(value = "/test",
- headers = {"X-SCT-Metadata-Transitive-a=11",
- "X-SCT-Metadata-Transitive-b=22",
- "X-SCT-Metadata-Transitive-c=33"})
+ @RequestMapping("/test")
String test();
}
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java
index ba5524c9a..78abb33ab 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/EncodeTransferMedataRestTemplateInterceptorTest.java
@@ -19,11 +19,12 @@
package com.tencent.cloud.metadata.core.intercepter;
import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
-import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor;
+import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +33,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -51,9 +55,6 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
properties = { "spring.config.location = classpath:application-test.yml" })
public class EncodeTransferMedataRestTemplateInterceptorTest {
- @Autowired
- private MetadataLocalProperties metadataLocalProperties;
-
@Autowired
private RestTemplate restTemplate;
@@ -61,30 +62,14 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
private int localServerPort;
@Test
- public void test1() {
-// HttpHeaders httpHeaders = new HttpHeaders();
-// httpHeaders.set(MetadataConstant.HeaderName.CUSTOM_METADATA,
-// "{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
-// HttpEntity httpEntity = new HttpEntity<>(httpHeaders);
-// String metadata = restTemplate
-// .exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET,
-// httpEntity, String.class)
-// .getBody();
-// Assertions.assertThat(metadata)
-// .isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
-// Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
-// .isEqualTo("1");
-// Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
-// .isEqualTo("2");
-// Assertions
-// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a"))
-// .isEqualTo("11");
-// Assertions
-// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"))
-// .isEqualTo("22");
-// Assertions
-// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c"))
-// .isEqualTo("33");
+ public void testTransitiveMetadataFromApplicationConfig() {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ HttpEntity httpEntity = new HttpEntity<>(httpHeaders);
+ String metadata = restTemplate
+ .exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET,
+ httpEntity, String.class)
+ .getBody();
+ Assertions.assertThat(metadata).isEqualTo("2");
}
@SpringBootApplication
@@ -100,7 +85,7 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
public String test(
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr)
throws UnsupportedEncodingException {
- return URLDecoder.decode(customMetadataStr, "UTF-8");
+ return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
}
}
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java
similarity index 97%
rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfiguration.java
rename to spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java
index 121ffcf6d..064bb081e 100644
--- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java
@@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
-package com.tencent.cloud.polaris.circuitbreaker;
+package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.common.constant.ContextConstant;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java
similarity index 95%
rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfiguration.java
rename to spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java
index 5b98172bb..ae699dead 100644
--- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java
@@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
-package com.tencent.cloud.polaris.circuitbreaker;
+package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor;
import com.tencent.cloud.polaris.context.PolarisContextAutoConfiguration;
@@ -40,8 +40,7 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
*
* @author Haotian Zhang
*/
-@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true",
- matchIfMissing = true)
+@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration")
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisRestTemplateAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisRestTemplateAutoConfiguration.java
new file mode 100644
index 000000000..ea99fce36
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisRestTemplateAutoConfiguration.java
@@ -0,0 +1,56 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.circuitbreaker.config;
+
+import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisResponseErrorHandler;
+import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateModifier;
+import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler;
+import com.tencent.cloud.polaris.context.PolarisContextAutoConfiguration;
+import com.tencent.polaris.api.core.ConsumerAPI;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author : wh
+ * @date : 2022/6/21 21:34
+ * @description: Auto configuration PolarisRestTemplateAutoConfiguration
+ */
+@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled",
+ havingValue = "true", matchIfMissing = true)
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
+public class PolarisRestTemplateAutoConfiguration {
+
+ @Bean
+ @ConditionalOnBean(RestTemplate.class)
+ public PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) {
+ return new PolarisRestTemplateResponseErrorHandler(consumerAPI, polarisResponseErrorHandler);
+ }
+
+ @Bean
+ @ConditionalOnBean(RestTemplate.class)
+ public PolarisRestTemplateModifier polarisRestTemplateBeanPostProcessor(PolarisRestTemplateResponseErrorHandler restTemplateResponseErrorHandler) {
+ return new PolarisRestTemplateModifier(restTemplateResponseErrorHandler);
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisResponseErrorHandler.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisResponseErrorHandler.java
new file mode 100644
index 000000000..5ddd1e6aa
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisResponseErrorHandler.java
@@ -0,0 +1,29 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
+
+import org.springframework.web.client.ResponseErrorHandler;
+
+/**
+ * @author : wh
+ * @date : 2022/6/21 19:12
+ * @description: errorHandler {@link ResponseErrorHandler}
+ */
+public interface PolarisResponseErrorHandler extends ResponseErrorHandler {
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateModifier.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateModifier.java
new file mode 100644
index 000000000..bd43913f4
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateModifier.java
@@ -0,0 +1,65 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
+
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author : wh
+ * @date : 2022/6/21 21:20
+ * @description: auto configuration RestTemplate Find the RestTemplate bean annotated with {@link LoadBalanced} and replace {@link org.springframework.web.client.ResponseErrorHandler}
+ * with {@link PolarisRestTemplateResponseErrorHandler}
+ */
+public class PolarisRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton {
+
+ private ApplicationContext applicationContext;
+
+ private final PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler;
+
+ public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) {
+ this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler;
+ }
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ Map beans = this.applicationContext.getBeansWithAnnotation(LoadBalanced.class);
+ if (!ObjectUtils.isEmpty(beans)) {
+ beans.forEach(this::initRestTemplate);
+ }
+ }
+
+ private void initRestTemplate(String beanName, Object bean) {
+ if (bean instanceof RestTemplate) {
+ RestTemplate restTemplate = (RestTemplate) bean;
+ restTemplate.setErrorHandler(polarisRestTemplateResponseErrorHandler);
+ }
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandler.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandler.java
new file mode 100644
index 000000000..e6e003d90
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandler.java
@@ -0,0 +1,111 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.Objects;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.util.ReflectionUtils;
+import com.tencent.polaris.api.core.ConsumerAPI;
+import com.tencent.polaris.api.pojo.RetStatus;
+import com.tencent.polaris.api.pojo.ServiceKey;
+import com.tencent.polaris.api.rpc.ServiceCallResult;
+import com.tencent.polaris.api.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.web.client.ResponseErrorHandler;
+
+/**
+ * @author : wh
+ * @date : 2022/6/21 17:25
+ * @description: Extend ResponseErrorHandler to get request information
+ */
+public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateResponseErrorHandler.class);
+
+ private static final String FileName = "connection";
+
+ private final ConsumerAPI consumerAPI;
+
+ private final PolarisResponseErrorHandler polarisResponseErrorHandler;
+
+
+ public PolarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI, PolarisResponseErrorHandler polarisResponseErrorHandler) {
+ this.consumerAPI = consumerAPI;
+ this.polarisResponseErrorHandler = polarisResponseErrorHandler;
+ }
+
+ @Override
+ public boolean hasError(ClientHttpResponse response) {
+ return true;
+ }
+
+ @Override
+ public void handleError(ClientHttpResponse response) throws IOException {
+ if (Objects.nonNull(polarisResponseErrorHandler)) {
+ if (polarisResponseErrorHandler.hasError(response)) {
+ polarisResponseErrorHandler.handleError(response);
+ }
+ }
+ }
+
+ public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
+ ServiceCallResult resultRequest = null;
+ try {
+ resultRequest = builderServiceCallResult(url, response);
+ }
+ catch (IOException e) {
+ LOG.error("Will report response of {} url {}", response, url, e);
+ throw e;
+ }
+ finally {
+ consumerAPI.updateServiceCallResult(resultRequest);
+ }
+ }
+
+ private ServiceCallResult builderServiceCallResult(URI uri, ClientHttpResponse response) throws IOException {
+ ServiceCallResult resultRequest = new ServiceCallResult();
+ String serviceName = uri.getHost();
+ resultRequest.setService(serviceName);
+ resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
+ resultRequest.setMethod(uri.getPath());
+ resultRequest.setRetStatus(RetStatus.RetSuccess);
+ String sourceNamespace = MetadataContext.LOCAL_NAMESPACE;
+ String sourceService = MetadataContext.LOCAL_SERVICE;
+ if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {
+ resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService));
+ }
+ HttpURLConnection connection = (HttpURLConnection) ReflectionUtils.getFieldValue(response, FileName);
+ URL url = connection.getURL();
+ resultRequest.setHost(url.getHost());
+ resultRequest.setPort(url.getPort());
+ if (response.getStatusCode().value() > 500) {
+ resultRequest.setRetStatus(RetStatus.RetFail);
+ }
+ return resultRequest;
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories
index 04fa47a13..229cc2af0 100644
--- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories
@@ -1,4 +1,6 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
- com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerBootstrapConfiguration
+ com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- com.tencent.cloud.polaris.circuitbreaker.PolarisFeignClientAutoConfiguration
+ com.tencent.cloud.polaris.circuitbreaker.config.PolarisFeignClientAutoConfiguration,\
+ com.tencent.cloud.polaris.circuitbreaker.config.PolarisRestTemplateAutoConfiguration
+
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java
index 42777a3b6..a5bd4f2e6 100644
--- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java
@@ -17,6 +17,7 @@
package com.tencent.cloud.polaris.circuitbreaker;
+import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java
index c83608478..2443949c9 100644
--- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java
@@ -17,6 +17,7 @@
package com.tencent.cloud.polaris.circuitbreaker;
+import com.tencent.cloud.polaris.circuitbreaker.config.PolarisFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor;
import com.tencent.cloud.polaris.context.PolarisContextAutoConfiguration;
import com.tencent.polaris.api.core.ConsumerAPI;
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisRestTemplateResponseErrorHandlerTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisRestTemplateResponseErrorHandlerTest.java
new file mode 100644
index 000000000..5daf30bc6
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisRestTemplateResponseErrorHandlerTest.java
@@ -0,0 +1,70 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.circuitbreaker;
+
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+
+import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler;
+import com.tencent.polaris.api.core.ConsumerAPI;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpMethod;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author : wh
+ * @date : 2022/6/22 09:00
+ * @description: Test for {@link PolarisRestTemplateResponseErrorHandler}.
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = PolarisRestTemplateResponseErrorHandlerTest.TestApplication.class,
+ properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"})
+public class PolarisRestTemplateResponseErrorHandlerTest {
+
+ @Test
+ public void handleError() throws Exception {
+ ConsumerAPI consumerAPI = mock(ConsumerAPI.class);
+ PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler = new PolarisRestTemplateResponseErrorHandler(consumerAPI, null);
+ URI uri = mock(URI.class);
+ when(uri.getPath()).thenReturn("/test");
+ when(uri.getHost()).thenReturn("host");
+ HttpURLConnection httpURLConnection = mock(HttpURLConnection.class);
+ URL url = mock(URL.class);
+ when(httpURLConnection.getURL()).thenReturn(url);
+ when(url.getHost()).thenReturn("127.0.0.1");
+ when(url.getPort()).thenReturn(8080);
+ when(httpURLConnection.getResponseCode()).thenReturn(200);
+ SimpleClientHttpResponseTest clientHttpResponse = new SimpleClientHttpResponseTest(httpURLConnection);
+ polarisRestTemplateResponseErrorHandler.handleError(uri, HttpMethod.GET, clientHttpResponse);
+ when(consumerAPI.unWatchService(null)).thenReturn(true);
+ }
+
+ @SpringBootApplication
+ protected static class TestApplication {
+
+ }
+}
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/SimpleClientHttpResponseTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/SimpleClientHttpResponseTest.java
new file mode 100644
index 000000000..ed76da17b
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/SimpleClientHttpResponseTest.java
@@ -0,0 +1,106 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.circuitbreaker;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.client.AbstractClientHttpResponse;
+import org.springframework.lang.Nullable;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
+
+
+/**
+ * @author : wh
+ * @date : 2022/6/22 09:00
+ * @description: mock {@link org.springframework.http.client.SimpleClientHttpResponse}
+ */
+public class SimpleClientHttpResponseTest extends AbstractClientHttpResponse {
+
+ private final HttpURLConnection connection;
+
+ @Nullable
+ private HttpHeaders headers;
+
+ @Nullable
+ private InputStream responseStream;
+
+
+ SimpleClientHttpResponseTest(HttpURLConnection connection) {
+ this.connection = connection;
+ }
+
+
+ @Override
+ public int getRawStatusCode() throws IOException {
+ return this.connection.getResponseCode();
+ }
+
+ @Override
+ public String getStatusText() throws IOException {
+ String result = this.connection.getResponseMessage();
+ return (result != null) ? result : "";
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ if (this.headers == null) {
+ this.headers = new HttpHeaders();
+ // Header field 0 is the status line for most HttpURLConnections, but not on GAE
+ String name = this.connection.getHeaderFieldKey(0);
+ if (StringUtils.hasLength(name)) {
+ this.headers.add(name, this.connection.getHeaderField(0));
+ }
+ int i = 1;
+ while (true) {
+ name = this.connection.getHeaderFieldKey(i);
+ if (!StringUtils.hasLength(name)) {
+ break;
+ }
+ this.headers.add(name, this.connection.getHeaderField(i));
+ i++;
+ }
+ }
+ return this.headers;
+ }
+
+ @Override
+ public InputStream getBody() throws IOException {
+ InputStream errorStream = this.connection.getErrorStream();
+ this.responseStream = (errorStream != null ? errorStream : this.connection.getInputStream());
+ return this.responseStream;
+ }
+
+ @Override
+ public void close() {
+ try {
+ if (this.responseStream == null) {
+ getBody();
+ }
+ StreamUtils.drain(this.responseStream);
+ this.responseStream.close();
+ }
+ catch (Exception ex) {
+ // ignore
+ }
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/pom.xml b/spring-cloud-starter-tencent-polaris-config/pom.xml
index 10b362a67..a7f0e5894 100644
--- a/spring-cloud-starter-tencent-polaris-config/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-config/pom.xml
@@ -11,6 +11,7 @@
4.0.0spring-cloud-starter-tencent-polaris-config
+ Spring Cloud Starter Tencent Polaris Config
@@ -64,5 +65,28 @@
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.mockito
+ mockito-inline
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ net.bytebuddy
+ byte-buddy
+ test
+
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConditionalOnConnectRemoteServerEnabled.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConditionalOnConnectRemoteServerEnabled.java
new file mode 100644
index 000000000..89283ae18
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConditionalOnConnectRemoteServerEnabled.java
@@ -0,0 +1,37 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+package com.tencent.cloud.polaris.config;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+/**
+ * Whether to connect to a remote server, suitable for local development mode.
+ *
+ * @author lepdou 2022-06-11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@ConditionalOnProperty(value = "spring.cloud.polaris.config.connect-remote-server", matchIfMissing = true)
+public @interface ConditionalOnConnectRemoteServerEnabled {
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java
index 79255de5f..2b61b69be 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java
@@ -20,7 +20,9 @@ package com.tencent.cloud.polaris.config;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceAutoRefresher;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
+import com.tencent.cloud.polaris.config.annotation.PolarisConfigAnnotationProcessor;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
+import com.tencent.cloud.polaris.config.listener.PolarisConfigChangeEventListener;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -46,4 +48,14 @@ public class PolarisConfigAutoConfiguration {
contextRefresher);
}
+ @Bean
+ public PolarisConfigAnnotationProcessor polarisConfigAnnotationProcessor() {
+ return new PolarisConfigAnnotationProcessor();
+ }
+
+ @Bean
+ public PolarisConfigChangeEventListener polarisConfigChangeEventListener() {
+ return new PolarisConfigChangeEventListener();
+ }
+
}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java
index d7e0eea0e..0a87c0e65 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java
@@ -50,24 +50,31 @@ public class PolarisConfigBootstrapAutoConfiguration {
}
@Bean
- public ConfigFileService configFileService(SDKContext sdkContext) {
- return ConfigFileServiceFactory.createConfigFileService(sdkContext);
+ public PolarisPropertySourceManager polarisPropertySourceManager() {
+ return new PolarisPropertySourceManager();
}
@Bean
- public PolarisPropertySourceManager polarisPropertySourceManager() {
- return new PolarisPropertySourceManager();
+ @ConditionalOnConnectRemoteServerEnabled
+ public ConfigFileService configFileService(SDKContext sdkContext) {
+ return ConfigFileServiceFactory.createConfigFileService(sdkContext);
}
@Bean
- public PolarisConfigFileLocator polarisConfigFileLocator(PolarisConfigProperties polarisConfigProperties,
- PolarisContextProperties polarisContextProperties, ConfigFileService configFileService,
- PolarisPropertySourceManager polarisPropertySourceManager, Environment environment) {
- return new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties, configFileService,
+ @ConditionalOnConnectRemoteServerEnabled
+ public PolarisConfigFileLocator polarisConfigFileLocator(
+ PolarisConfigProperties polarisConfigProperties,
+ PolarisContextProperties polarisContextProperties,
+ ConfigFileService configFileService,
+ PolarisPropertySourceManager polarisPropertySourceManager,
+ Environment environment) {
+ return new PolarisConfigFileLocator(polarisConfigProperties,
+ polarisContextProperties, configFileService,
polarisPropertySourceManager, environment);
}
@Bean
+ @ConditionalOnConnectRemoteServerEnabled
public ConfigurationModifier configurationModifier(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
return new ConfigurationModifier(polarisConfigProperties, polarisContextProperties);
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java
index a8182ec09..b23d6a132 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java
@@ -80,13 +80,14 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
CompositePropertySource compositePropertySource = new CompositePropertySource(
POLARIS_CONFIG_PROPERTY_SOURCE_NAME);
+ // load spring boot default config files
+ initInternalConfigFiles(compositePropertySource);
+
+ // load custom config files
List configFileGroups = polarisConfigProperties.getGroups();
if (CollectionUtils.isEmpty(configFileGroups)) {
return compositePropertySource;
}
-
- initInternalConfigFiles(compositePropertySource);
-
initCustomPolarisConfigFiles(compositePropertySource, configFileGroups);
return compositePropertySource;
@@ -181,7 +182,8 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
private PolarisPropertySource loadPolarisPropertySource(String namespace, String group, String fileName) {
ConfigKVFile configKVFile;
// unknown extension is resolved as properties file
- if (ConfigFileFormat.isPropertyFile(fileName) || ConfigFileFormat.isUnknownFile(fileName)) {
+ if (ConfigFileFormat.isPropertyFile(fileName)
+ || ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
}
else if (ConfigFileFormat.isYamlFile(fileName)) {
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigAnnotationProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigAnnotationProcessor.java
new file mode 100644
index 000000000..349799f69
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigAnnotationProcessor.java
@@ -0,0 +1,105 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.config.annotation;
+
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
+import com.tencent.cloud.polaris.config.listener.ConfigChangeListener;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.Ordered;
+import org.springframework.core.PriorityOrdered;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.lang.NonNull;
+import org.springframework.util.ReflectionUtils;
+
+import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.addChangeListener;
+
+/**
+ * {@link PolarisConfigAnnotationProcessor} implementation for spring .
+ *
Refer to the Apollo project implementation:
+ *
+ * ApolloAnnotationProcessor
+ * @author Palmer Xu 2022-06-07
+ */
+public class PolarisConfigAnnotationProcessor implements BeanPostProcessor, PriorityOrdered {
+
+ @Override
+ public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName)
+ throws BeansException {
+ Class> clazz = bean.getClass();
+ for (Method method : findAllMethod(clazz)) {
+ this.processPolarisConfigChangeListener(bean, method);
+ }
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+
+ private List findAllMethod(Class> clazz) {
+ final List res = new LinkedList<>();
+ ReflectionUtils.doWithMethods(clazz, res::add);
+ return res;
+ }
+
+ private void processPolarisConfigChangeListener(final Object bean, final Method method) {
+ PolarisConfigKVFileChangeListener annotation = AnnotationUtils
+ .findAnnotation(method, PolarisConfigKVFileChangeListener.class);
+ if (annotation == null) {
+ return;
+ }
+ Class>[] parameterTypes = method.getParameterTypes();
+ Preconditions.checkArgument(parameterTypes.length == 1,
+ "Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length,
+ method);
+ Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]),
+ "Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0],
+ method);
+
+ ReflectionUtils.makeAccessible(method);
+ String[] annotatedInterestedKeys = annotation.interestedKeys();
+ String[] annotatedInterestedKeyPrefixes = annotation.interestedKeyPrefixes();
+
+ ConfigChangeListener configChangeListener = changeEvent -> ReflectionUtils.invokeMethod(method, bean, changeEvent);
+
+ Set interestedKeys =
+ annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null;
+ Set interestedKeyPrefixes =
+ annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes)
+ : null;
+
+ addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes);
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigKVFileChangeListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigKVFileChangeListener.java
new file mode 100644
index 000000000..6acbf2336
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigKVFileChangeListener.java
@@ -0,0 +1,58 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.config.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Configuring the change listener annotation.
+ *
Refer to the Apollo project implementation:
+ *
+ * ApolloAnnotationProcessor
+ * @author Palmer Xu 2022-05-31
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Documented
+public @interface PolarisConfigKVFileChangeListener {
+
+ /**
+ * The keys interested in the listener, will only be notified if any of the interested keys is changed.
+ *
+ * If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when any key is changed.
+ * @return interested keys in the listener
+ */
+ String[] interestedKeys() default {};
+
+ /**
+ * The key prefixes that the listener is interested in, will be notified if and only if the changed keys start with anyone of the prefixes.
+ * The prefixes will simply be used to determine whether the {@code listener} should be notified or not using {@code changedKey.startsWith(prefix)}.
+ * e.g. "spring." means that {@code listener} is interested in keys that starts with "spring.", such as "spring.banner", "spring.jpa", etc.
+ * and "application" means that {@code listener} is interested in keys that starts with "application", such as "applicationName", "application.port", etc.
+ *
+ * If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when whatever key is changed.
+ * @return interested key-prefixed in the listener
+ */
+ String[] interestedKeyPrefixes() default {};
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeEvent.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeEvent.java
new file mode 100644
index 000000000..119a5ce4c
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeEvent.java
@@ -0,0 +1,88 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.config.listener;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
+
+/**
+ * A change event when config is changed .
+ *
+ * @author Palmer Xu 2022-06-07
+ */
+public final class ConfigChangeEvent {
+
+ /**
+ * all changes keys map.
+ */
+ private final Map changes;
+
+ /**
+ * all interested changed keys.
+ */
+ private final Set interestedChangedKeys;
+
+ /**
+ * Config Change Event Constructor.
+ * @param changes all changes keys map
+ * @param interestedChangedKeys all interested changed keys
+ */
+ public ConfigChangeEvent(Map changes, Set interestedChangedKeys) {
+ this.changes = changes;
+ this.interestedChangedKeys = interestedChangedKeys;
+ }
+
+ /**
+ * Get the keys changed.
+ * @return the list of the keys
+ */
+ public Set changedKeys() {
+ return changes.keySet();
+ }
+
+ /**
+ * Get a specific change instance for the key specified.
+ * @param key the changed key
+ * @return the change instance
+ */
+ public ConfigPropertyChangeInfo getChange(String key) {
+ return changes.get(key);
+ }
+
+ /**
+ * Check whether the specified key is changed .
+ * @param key the key
+ * @return true if the key is changed, false otherwise.
+ */
+ public boolean isChanged(String key) {
+ return changes.containsKey(key);
+ }
+
+ /**
+ * Maybe subclass override this method.
+ *
+ * @return interested and changed keys
+ */
+ public Set interestedChangedKeys() {
+ return interestedChangedKeys;
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListener.java
new file mode 100644
index 000000000..14ab64a32
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListener.java
@@ -0,0 +1,35 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.config.listener;
+
+/**
+ * Configuring the change listener interface.
+ *
+ * @author Palmer Xu 2022-05-31
+ */
+public interface ConfigChangeListener {
+
+ /**
+ * Invoked when there is any config change for the namespace.
+ *
+ * @param changeEvent the event for this change
+ */
+ void onChange(ConfigChangeEvent changeEvent);
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigChangeEventListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigChangeEventListener.java
new file mode 100644
index 000000000..0a55255d5
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigChangeEventListener.java
@@ -0,0 +1,111 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.config.listener;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.google.common.collect.Maps;
+import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
+
+import org.springframework.boot.context.event.ApplicationStartedEvent;
+import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.lang.NonNull;
+
+import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.fireConfigChange;
+import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.initialize;
+import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.merge;
+
+/**
+ * Polaris Config Change Event Listener .
+ *
+ * @author Elve.Xu 2022-06-08
+ */
+public final class PolarisConfigChangeEventListener implements ApplicationListener {
+
+ private static final AtomicBoolean started = new AtomicBoolean();
+
+ /**
+ * Handle an application event.
+ *
+ * @param event the event to respond to
+ */
+ @Override
+ public void onApplicationEvent(@NonNull ApplicationEvent event) {
+
+ // Initialize application all environment properties .
+ if (event instanceof ApplicationStartedEvent && started.compareAndSet(false, true)) {
+ ApplicationStartedEvent applicationStartedEvent = (ApplicationStartedEvent) event;
+ ConfigurableEnvironment environment = applicationStartedEvent.getApplicationContext().getEnvironment();
+ Map ret = loadEnvironmentProperties(environment);
+ if (!ret.isEmpty()) {
+ initialize(ret);
+ }
+ }
+
+ // Process Environment Change Event .
+ if (event instanceof EnvironmentChangeEvent) {
+ EnvironmentChangeEvent environmentChangeEvent = (EnvironmentChangeEvent) event;
+ ConfigurableApplicationContext context = (ConfigurableApplicationContext) environmentChangeEvent.getSource();
+ ConfigurableEnvironment environment = context.getEnvironment();
+ Map ret = loadEnvironmentProperties(environment);
+ Map changes = merge(ret);
+ fireConfigChange(changes.keySet(), Maps.newHashMap(changes));
+ changes.clear();
+ }
+ }
+
+ /**
+ * Try load all application environment config properties .
+ * @param environment application environment instance of {@link Environment}
+ * @return properties
+ */
+ @SuppressWarnings("unchecked")
+ private Map loadEnvironmentProperties(ConfigurableEnvironment environment) {
+ Map ret = Maps.newHashMap();
+ MutablePropertySources sources = environment.getPropertySources();
+ sources.iterator().forEachRemaining(propertySource -> {
+ Object o = propertySource.getSource();
+ if (o instanceof Map) {
+ for (Map.Entry entry : ((Map) o).entrySet()) {
+ String key = entry.getKey();
+ String value = environment.getProperty(key);
+ ret.put(key, value);
+ }
+ }
+ else if (o instanceof Collection) {
+ int count = 0;
+ Collection