Merge branch 'main' into optimization_fefreshScope

# Conflicts:
#	CHANGELOG.md
#	spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java
#	spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java
#	spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java
#	spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/JacksonUtils.java
pull/326/head
weihu 2 years ago
commit b5ca59cf46

@ -5,12 +5,17 @@ name: Codecov
on: on:
push: push:
branches: [ main ] branches:
- main
- 2021.0
- 2020.0
- greenwich
pull_request: pull_request:
branches: [ main ] branches:
- main
permissions: - 2021.0
contents: write - 2020.0
- greenwich
jobs: jobs:
build: build:

@ -22,7 +22,7 @@ jobs:
strategy: strategy:
matrix: matrix:
java: [ 8, 11, 17 ] java: [ 8, 11, 17 ]
os: [ 'windows-latest', 'macos-latest', 'ubuntu-latest' ] os: [ 'windows-latest', 'ubuntu-latest' ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}

@ -21,8 +21,20 @@
- [Fix the current limiting effect is that other requests cannot be processed when queuing at a constant speed](https://github.com/Tencent/spring-cloud-tencent/pull/316) - [Fix the current limiting effect is that other requests cannot be processed when queuing at a constant speed](https://github.com/Tencent/spring-cloud-tencent/pull/316)
- [Fix config file format misspell](https://github.com/Tencent/spring-cloud-tencent/pull/319) - [Fix config file format misspell](https://github.com/Tencent/spring-cloud-tencent/pull/319)
- [UT: improve test coverage for load balancer unit test](https://github.com/Tencent/spring-cloud-tencent/pull/325) - [UT: improve test coverage for load balancer unit test](https://github.com/Tencent/spring-cloud-tencent/pull/325)
- [Feature: Optimize polaris load balancer test code format](https://github.com/Tencent/spring-cloud-tencent/pull/333) - [optimize polaris load balancer test code format](https://github.com/Tencent/spring-cloud-tencent/pull/333)
- [feat:Add GitHub action of codecov.yml.](https://github.com/Tencent/spring-cloud-tencent/pull/328) - [feat:Add GitHub action of codecov.yml.](https://github.com/Tencent/spring-cloud-tencent/pull/328)
- [Feature: add spring cloud tencent logo](https://github.com/Tencent/spring-cloud-tencent/pull/329) - [Feature: add spring cloud tencent logo](https://github.com/Tencent/spring-cloud-tencent/pull/329)
- [Feature: Optimize static metadata manager](https://github.com/Tencent/spring-cloud-tencent/pull/327) - [Feature: Optimize static metadata manager](https://github.com/Tencent/spring-cloud-tencent/pull/327)
- [Feature: support actuator for sct core components](https://github.com/Tencent/spring-cloud-tencent/pull/343)
- [test:update junit of metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/340)
- [Optimize code style & unit test case](https://github.com/Tencent/spring-cloud-tencent/pull/336)
- [rm code: Condition 'null != interceptors' is always 'true' ](https://github.com/Tencent/spring-cloud-tencent/pull/342)
- [fix: shutdown thread pool before the container closes](https://github.com/Tencent/spring-cloud-tencent/pull/353)
- [docs:update logo in README.](https://github.com/Tencent/spring-cloud-tencent/pull/358)
- [Refator JacksonUtils and JacksonUtilsTest](https://github.com/Tencent/spring-cloud-tencent/pull/365)
- [refactor:optimize project and code.](https://github.com/Tencent/spring-cloud-tencent/pull/368)
- [docs: Fix javadoc <br /> error](https://github.com/Tencent/spring-cloud-tencent/pull/371)
- [UT: add Polaris LoadBalancer unit test](https://github.com/Tencent/spring-cloud-tencent/pull/373)
- [docs:update docs](https://github.com/Tencent/spring-cloud-tencent/pull/378)
- [docs:optimize example](https://github.com/Tencent/spring-cloud-tencent/pull/385)
- [Feature: optimization refreshScope](https://github.com/Tencent/spring-cloud-tencent/pull/326) - [Feature: optimization refreshScope](https://github.com/Tencent/spring-cloud-tencent/pull/326)

@ -1,4 +1,4 @@
<img src="https://user-images.githubusercontent.com/4991116/176166888-9c8a4074-ba42-41b8-93db-d6e95b16656b.png" width="60%" height="60%" alt="Spring-Cloud-Tencent-Logo"/> <img src="https://github.com/Tencent/spring-cloud-tencent/raw/main/doc/logo/rectangle-white.png" width="60%" height="60%" alt="Spring-Cloud-Tencent-Logo"/>
[![Wiki](https://badgen.net/badge/icon/wiki?icon=wiki&label)](https://github.com/Tencent/spring-cloud-tencent/wiki) [![Wiki](https://badgen.net/badge/icon/wiki?icon=wiki&label)](https://github.com/Tencent/spring-cloud-tencent/wiki)
[![Build Status](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml/badge.svg)](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml) [![Build Status](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml/badge.svg)](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml)

@ -1,4 +1,4 @@
<img src="https://user-images.githubusercontent.com/4991116/176166888-9c8a4074-ba42-41b8-93db-d6e95b16656b.png" width="60%" height="60%" alt="Spring-Cloud-Tencent-Logo"/> <img src="https://github.com/Tencent/spring-cloud-tencent/raw/main/doc/logo/rectangle-white.png" width="60%" height="60%" alt="Spring-Cloud-Tencent-Logo"/>
[![Wiki](https://badgen.net/badge/icon/wiki?icon=wiki&label)](https://github.com/Tencent/spring-cloud-tencent/wiki) [![Wiki](https://badgen.net/badge/icon/wiki?icon=wiki&label)](https://github.com/Tencent/spring-cloud-tencent/wiki)

@ -0,0 +1,4 @@
# Change Log
---
- [fix not load application.yml bug & fix guava version conflict bug](https://github.com/Tencent/spring-cloud-tencent/pull/284)

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="424px" height="82px" viewBox="0 0 424 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>长款的 svg格式透明</title>
<g id="长款的-svg格式透明" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="矩形" x="0" y="0" width="424" height="82"></rect>
<text id="Spring-Cloud-Tencent" font-family="TencentSansW7, TencentSans" font-size="34" font-weight="bold" letter-spacing="-0.50303" fill="#000000">
<tspan x="83.8428418" y="49.8255109">Spring Cloud Tencent</tspan>
</text>
<polygon id="路径-2备份-10" fill="#95D2F7" points="43.9206195 11.8255109 43.9206195 21.8255109 52.6095084 16.8255109"></polygon>
<polygon id="矩形备份-25" fill="#7F98FF" points="24.9833459 32.3489782 48.8428418 46.1744891 48.8428418 73.8255109 24.9833459 60"></polygon>
<polygon id="矩形备份-26" fill="#7F98FF" transform="translate(55.772590, 29.412755) scale(-1, 1) translate(-55.772590, -29.412755) " points="43.8428418 8 67.7023376 21.8255109 67.7023376 50.8255109 43.8428418 37"></polygon>
<polygon id="矩形备份-27" fill="#95D2F7" transform="translate(24.983346, 21.523467) scale(-1, 1) translate(-24.983346, -21.523467) " points="24.9833459 10.5234673 43.9666917 21.5234673 24.9833459 32.5234673 6 21.5234673"></polygon>
<polygon id="路径-3" fill="#4065FF" points="43.8428418 43.2977777 43.8428418 50.8255109 48.8428418 47.9799656 48.8428418 46.1744891"></polygon>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="424px" height="82px" viewBox="0 0 424 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>长款的 svg 格式的白底</title>
<g id="长款的-svg-格式的白底" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="矩形" fill="#FFFFFF" x="0" y="0" width="424" height="82"></rect>
<rect id="矩形" fill="#FFFFFF" x="6" y="8" width="412" height="66"></rect>
<text id="Spring-Cloud-Tencent" font-family="TencentSansW7, TencentSans" font-size="34" font-weight="bold" letter-spacing="-0.50303" fill="#000000">
<tspan x="83.8428418" y="49.8255109">Spring Cloud Tencent</tspan>
</text>
<polygon id="路径-2备份-10" fill="#95D2F7" points="43.9206195 11.8255109 43.9206195 21.8255109 52.6095084 16.8255109"></polygon>
<polygon id="矩形备份-25" fill="#7F98FF" points="24.9833459 32.3489782 48.8428418 46.1744891 48.8428418 73.8255109 24.9833459 60"></polygon>
<polygon id="矩形备份-26" fill="#7F98FF" transform="translate(55.772590, 29.412755) scale(-1, 1) translate(-55.772590, -29.412755) " points="43.8428418 8 67.7023376 21.8255109 67.7023376 50.8255109 43.8428418 37"></polygon>
<polygon id="矩形备份-27" fill="#95D2F7" transform="translate(24.983346, 21.523467) scale(-1, 1) translate(-24.983346, -21.523467) " points="24.9833459 10.5234673 43.9666917 21.5234673 24.9833459 32.5234673 6 21.5234673"></polygon>
<polygon id="路径-3" fill="#4065FF" points="43.8428418 43.2977777 43.8428418 50.8255109 48.8428418 47.9799656 48.8428418 46.1744891"></polygon>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -68,12 +68,10 @@ public class MetadataTransferAutoConfiguration {
@Bean @Bean
public FilterRegistrationBean<DecodeTransferMetadataServletFilter> metadataServletFilterRegistrationBean( public FilterRegistrationBean<DecodeTransferMetadataServletFilter> metadataServletFilterRegistrationBean(
DecodeTransferMetadataServletFilter decodeTransferMetadataServletFilter) { DecodeTransferMetadataServletFilter decodeTransferMetadataServletFilter) {
FilterRegistrationBean<DecodeTransferMetadataServletFilter> filterRegistrationBean = new FilterRegistrationBean<>( FilterRegistrationBean<DecodeTransferMetadataServletFilter> filterRegistrationBean =
decodeTransferMetadataServletFilter); new FilterRegistrationBean<>(decodeTransferMetadataServletFilter);
filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST);
REQUEST); filterRegistrationBean.setOrder(MetadataConstant.OrderConstant.WEB_FILTER_ORDER);
filterRegistrationBean
.setOrder(MetadataConstant.OrderConstant.WEB_FILTER_ORDER);
return filterRegistrationBean; return filterRegistrationBean;
} }
@ -158,30 +156,25 @@ public class MetadataTransferAutoConfiguration {
BeanPostProcessor encodeTransferMetadataRestTemplatePostProcessor( BeanPostProcessor encodeTransferMetadataRestTemplatePostProcessor(
EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor) { EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor) {
// Coping with multiple bean injection scenarios // Coping with multiple bean injection scenarios
Map<String, RestTemplate> beans = this.context Map<String, RestTemplate> beans = this.context.getBeansOfType(RestTemplate.class);
.getBeansOfType(RestTemplate.class);
// If the restTemplate has been created when the // If the restTemplate has been created when the
// MetadataRestTemplatePostProcessor Bean // MetadataRestTemplatePostProcessor Bean
// is initialized, then manually set the interceptor. // is initialized, then manually set the interceptor.
if (!CollectionUtils.isEmpty(beans)) { if (!CollectionUtils.isEmpty(beans)) {
for (RestTemplate restTemplate : beans.values()) { for (RestTemplate restTemplate : beans.values()) {
List<ClientHttpRequestInterceptor> interceptors = restTemplate List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
.getInterceptors();
// Avoid setting interceptor repeatedly. // Avoid setting interceptor repeatedly.
if (null != interceptors && !interceptors if (!interceptors.contains(encodeTransferMedataRestTemplateInterceptor)) {
.contains(encodeTransferMedataRestTemplateInterceptor)) {
interceptors.add(encodeTransferMedataRestTemplateInterceptor); interceptors.add(encodeTransferMedataRestTemplateInterceptor);
restTemplate.setInterceptors(interceptors); restTemplate.setInterceptors(interceptors);
} }
} }
} }
return new EncodeTransferMetadataRestTemplatePostProcessor( return new EncodeTransferMetadataRestTemplatePostProcessor(encodeTransferMedataRestTemplateInterceptor);
encodeTransferMedataRestTemplateInterceptor);
} }
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
throws BeansException {
this.context = applicationContext; this.context = applicationContext;
} }
@ -204,21 +197,15 @@ public class MetadataTransferAutoConfiguration {
public Object postProcessAfterInitialization(Object bean, String beanName) { public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof RestTemplate) { if (bean instanceof RestTemplate) {
RestTemplate restTemplate = (RestTemplate) bean; RestTemplate restTemplate = (RestTemplate) bean;
List<ClientHttpRequestInterceptor> interceptors = restTemplate List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
.getInterceptors();
// Avoid setting interceptor repeatedly. // Avoid setting interceptor repeatedly.
if (null != interceptors && !interceptors if (!interceptors.contains(encodeTransferMedataRestTemplateInterceptor)) {
.contains(encodeTransferMedataRestTemplateInterceptor)) { interceptors.add(this.encodeTransferMedataRestTemplateInterceptor);
interceptors
.add(this.encodeTransferMedataRestTemplateInterceptor);
restTemplate.setInterceptors(interceptors); restTemplate.setInterceptors(interceptors);
} }
} }
return bean; return bean;
} }
} }
} }
} }

@ -32,13 +32,16 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* resolve custom transitive metadata from request. * Resolve custom transitive metadata from request.
*@author lepdou 2022-05-20 *
* @author lepdou 2022-05-20
*/ */
public class CustomTransitiveMetadataResolver { public final class CustomTransitiveMetadataResolver {
private static final String TRANSITIVE_HEADER_PREFIX = "X-SCT-Metadata-Transitive-"; private static final String TRANSITIVE_HEADER_PREFIX = "X-SCT-Metadata-Transitive-";
private static final int TRANSITIVE_HEADER_PREFIX_LENGTH = TRANSITIVE_HEADER_PREFIX.length(); private static final int TRANSITIVE_HEADER_PREFIX_LENGTH = TRANSITIVE_HEADER_PREFIX.length();
private CustomTransitiveMetadataResolver() {
}
public static Map<String, String> resolve(ServerWebExchange exchange) { public static Map<String, String> resolve(ServerWebExchange exchange) {
Map<String, String> result = new HashMap<>(); Map<String, String> result = new HashMap<>();
@ -46,11 +49,9 @@ public class CustomTransitiveMetadataResolver {
HttpHeaders headers = exchange.getRequest().getHeaders(); HttpHeaders headers = exchange.getRequest().getHeaders();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) { for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
if (StringUtils.isNotBlank(key)
if (StringUtils.isNotBlank(key) && && StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX)
StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX)
&& !CollectionUtils.isEmpty(entry.getValue())) { && !CollectionUtils.isEmpty(entry.getValue())) {
String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH); String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH);
result.put(sourceKey, entry.getValue().get(0)); result.put(sourceKey, entry.getValue().get(0));
} }
@ -66,10 +67,9 @@ public class CustomTransitiveMetadataResolver {
while (headers.hasMoreElements()) { while (headers.hasMoreElements()) {
String key = headers.nextElement(); String key = headers.nextElement();
if (StringUtils.isNotBlank(key) && if (StringUtils.isNotBlank(key)
StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX) && StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX)
&& StringUtils.isNotBlank(request.getHeader(key))) { && StringUtils.isNotBlank(request.getHeader(key))) {
String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH); String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH);
result.put(sourceKey, request.getHeader(key)); result.put(sourceKey, request.getHeader(key));
} }

@ -47,8 +47,7 @@ import org.springframework.web.server.WebFilterChain;
*/ */
public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered { public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered {
private static final Logger LOG = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataReactiveFilter.class);
.getLogger(DecodeTransferMetadataReactiveFilter.class);
@Override @Override
public int getOrder() { public int getOrder() {

@ -49,8 +49,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
@Order(MetadataConstant.OrderConstant.WEB_FILTER_ORDER) @Order(MetadataConstant.OrderConstant.WEB_FILTER_ORDER)
public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class);
.getLogger(DecodeTransferMetadataServletFilter.class);
@Override @Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, protected void doFilterInternal(HttpServletRequest httpServletRequest,
@ -75,8 +74,7 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
private Map<String, String> getInternalTransitiveMetadata(HttpServletRequest httpServletRequest) { private Map<String, String> getInternalTransitiveMetadata(HttpServletRequest httpServletRequest) {
// Get custom metadata string from http header. // Get custom metadata string from http header.
String customMetadataStr = httpServletRequest String customMetadataStr = httpServletRequest.getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA);
.getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA);
try { try {
if (StringUtils.hasText(customMetadataStr)) { if (StringUtils.hasText(customMetadataStr)) {
customMetadataStr = URLDecoder.decode(customMetadataStr, StandardCharsets.UTF_8.name()); customMetadataStr = URLDecoder.decode(customMetadataStr, StandardCharsets.UTF_8.name());
@ -90,5 +88,4 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
// create custom metadata. // create custom metadata.
return JacksonUtils.deserialize2Map(customMetadataStr); return JacksonUtils.deserialize2Map(customMetadataStr);
} }
} }

@ -45,8 +45,7 @@ import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUST
*/ */
public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, Ordered { public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, Ordered {
private static final Logger LOG = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(EncodeTransferMedataFeignInterceptor.class);
.getLogger(EncodeTransferMedataFeignInterceptor.class);
@Override @Override
public int getOrder() { public int getOrder() {

@ -42,8 +42,7 @@ import org.springframework.util.CollectionUtils;
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class EncodeTransferMedataRestTemplateInterceptor public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRequestInterceptor, Ordered {
implements ClientHttpRequestInterceptor, Ordered {
@Override @Override
public int getOrder() { public int getOrder() {
@ -71,5 +70,4 @@ public class EncodeTransferMedataRestTemplateInterceptor
return clientHttpRequestExecution.execute(httpRequest, bytes); return clientHttpRequestExecution.execute(httpRequest, bytes);
} }
} }

@ -45,8 +45,7 @@ import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.
*/ */
public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered { public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
private static final int METADATA_SCG_FILTER_ORDER = LOAD_BALANCER_CLIENT_FILTER_ORDER private static final int METADATA_SCG_FILTER_ORDER = LOAD_BALANCER_CLIENT_FILTER_ORDER + 1;
+ 1;
@Override @Override
public int getOrder() { public int getOrder() {
@ -59,8 +58,7 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
ServerHttpRequest.Builder builder = exchange.getRequest().mutate(); ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
// get metadata of current thread // get metadata of current thread
MetadataContext metadataContext = exchange MetadataContext metadataContext = exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT);
.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT);
// add new metadata and cover old // add new metadata and cover old
if (metadataContext == null) { if (metadataContext == null) {
@ -80,5 +78,4 @@ public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered {
return chain.filter(exchange.mutate().request(builder.build()).build()); return chain.filter(exchange.mutate().request(builder.build()).build());
} }
} }

@ -70,13 +70,11 @@ public class EncodeTransferMetadataZuulFilter extends ZuulFilter {
if (!CollectionUtils.isEmpty(customMetadata)) { if (!CollectionUtils.isEmpty(customMetadata)) {
String metadataStr = JacksonUtils.serialize2Json(customMetadata); String metadataStr = JacksonUtils.serialize2Json(customMetadata);
try { try {
requestContext.addZuulRequestHeader( requestContext.addZuulRequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA,
MetadataConstant.HeaderName.CUSTOM_METADATA,
URLEncoder.encode(metadataStr, StandardCharsets.UTF_8.name())); URLEncoder.encode(metadataStr, StandardCharsets.UTF_8.name()));
} }
catch (UnsupportedEncodingException e) { catch (UnsupportedEncodingException e) {
requestContext.addZuulRequestHeader( requestContext.addZuulRequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA, metadataStr);
MetadataConstant.HeaderName.CUSTOM_METADATA, metadataStr);
} }
} }
return null; return null;

@ -55,7 +55,9 @@ public class MetadataTransferAutoConfigurationTest {
Assertions.assertThat(context).hasSingleBean( Assertions.assertThat(context).hasSingleBean(
EncodeTransferMedataRestTemplateInterceptor.class); EncodeTransferMedataRestTemplateInterceptor.class);
Assertions.assertThat(context).hasSingleBean( Assertions.assertThat(context).hasSingleBean(
MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.EncodeTransferMetadataRestTemplatePostProcessor.class); MetadataTransferAutoConfiguration
.MetadataTransferRestTemplateConfig
.EncodeTransferMetadataRestTemplatePostProcessor.class);
Assertions.assertThat(context).hasSingleBean( Assertions.assertThat(context).hasSingleBean(
MetadataTransferAutoConfiguration.MetadataTransferZuulFilterConfig.class); MetadataTransferAutoConfiguration.MetadataTransferZuulFilterConfig.class);
Assertions.assertThat(context) Assertions.assertThat(context)
@ -65,5 +67,4 @@ public class MetadataTransferAutoConfigurationTest {
Assertions.assertThat(context).hasSingleBean(GlobalFilter.class); Assertions.assertThat(context).hasSingleBean(GlobalFilter.class);
}); });
} }
} }

@ -13,14 +13,12 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.metadata; package com.tencent.cloud.metadata.core;
import java.util.Map; import java.util.Map;
import com.tencent.cloud.metadata.core.CustomTransitiveMetadataResolver;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Test; import org.junit.Test;
@ -29,6 +27,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.mock.web.server.MockServerWebExchange;
/** /**
* Test for {@link CustomTransitiveMetadataResolver}.
*
* @author quan * @author quan
*/ */
public class CustomTransitiveMetadataResolverTest { public class CustomTransitiveMetadataResolverTest {

@ -13,14 +13,12 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.metadata; package com.tencent.cloud.metadata.core;
import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.DecodeTransferMetadataReactiveFilter;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -38,6 +36,8 @@ import org.springframework.web.server.WebFilterChain;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.MOCK; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.MOCK;
/** /**
* Test for {@link DecodeTransferMetadataReactiveFilter}.
*
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ -80,5 +80,4 @@ public class DecodeTransferMetadataReactiveFilterTest {
.isEqualTo("2"); .isEqualTo("2");
Assertions.assertThat(metadataLocalProperties.getContent().get("c")).isNull(); Assertions.assertThat(metadataLocalProperties.getContent().get("c")).isNull();
} }
} }

@ -13,10 +13,9 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.metadata; package com.tencent.cloud.metadata.core;
import java.io.IOException; import java.io.IOException;
@ -25,7 +24,6 @@ import javax.servlet.ServletException;
import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.DecodeTransferMetadataServletFilter;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -40,6 +38,8 @@ import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/** /**
* Test for {@link DecodeTransferMetadataServletFilter}.
*
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ -68,10 +68,8 @@ public class DecodeTransferMetadataServletFilterTest {
request.addHeader(MetadataConstant.HeaderName.CUSTOM_METADATA, "{\"c\": \"3\"}"); request.addHeader(MetadataConstant.HeaderName.CUSTOM_METADATA, "{\"c\": \"3\"}");
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
metadataServletFilter.doFilter(request, response, filterChain); metadataServletFilter.doFilter(request, response, filterChain);
Assertions.assertThat(metadataLocalProperties.getContent().get("a")) Assertions.assertThat(metadataLocalProperties.getContent().get("a")).isEqualTo("1");
.isEqualTo("1"); Assertions.assertThat(metadataLocalProperties.getContent().get("b")).isEqualTo("2");
Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
.isEqualTo("2");
Assertions.assertThat(metadataLocalProperties.getContent().get("c")).isNull(); Assertions.assertThat(metadataLocalProperties.getContent().get("c")).isNull();
} }
@ -79,5 +77,4 @@ public class DecodeTransferMetadataServletFilterTest {
protected static class TestApplication { protected static class TestApplication {
} }
} }

@ -13,10 +13,9 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.metadata.core.intercepter; package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@ -24,7 +23,6 @@ import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor;
import feign.RequestInterceptor; import feign.RequestInterceptor;
import feign.RequestTemplate; import feign.RequestTemplate;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
@ -38,7 +36,6 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -68,10 +65,8 @@ public class EncodeTransferMedataFeignInterceptorTest {
public void testTransitiveMetadataFromApplicationConfig() { public void testTransitiveMetadataFromApplicationConfig() {
String metadata = testFeign.test(); String metadata = testFeign.test();
Assertions.assertThat(metadata).isEqualTo("2"); Assertions.assertThat(metadata).isEqualTo("2");
Assertions.assertThat(metadataLocalProperties.getContent().get("a")) Assertions.assertThat(metadataLocalProperties.getContent().get("a")).isEqualTo("1");
.isEqualTo("1"); Assertions.assertThat(metadataLocalProperties.getContent().get("b")).isEqualTo("2");
Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
.isEqualTo("2");
} }
@SpringBootApplication @SpringBootApplication
@ -80,9 +75,7 @@ public class EncodeTransferMedataFeignInterceptorTest {
protected static class TestApplication { protected static class TestApplication {
@RequestMapping("/test") @RequestMapping("/test")
public String test( public String test() throws UnsupportedEncodingException {
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr)
throws UnsupportedEncodingException {
return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"); return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
} }
@ -91,7 +84,6 @@ public class EncodeTransferMedataFeignInterceptorTest {
@RequestMapping("/test") @RequestMapping("/test")
String test(); String test();
} }
@Configuration @Configuration
@ -102,9 +94,6 @@ public class EncodeTransferMedataFeignInterceptorTest {
template.header(MetadataConstant.HeaderName.CUSTOM_METADATA, template.header(MetadataConstant.HeaderName.CUSTOM_METADATA,
"{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}"); "{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
} }
} }
} }
} }

@ -13,17 +13,14 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.metadata.core.intercepter; package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -37,7 +34,6 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -52,7 +48,8 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, @SpringBootTest(webEnvironment = RANDOM_PORT,
classes = EncodeTransferMedataRestTemplateInterceptorTest.TestApplication.class, classes = EncodeTransferMedataRestTemplateInterceptorTest.TestApplication.class,
properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) properties = {"spring.config.location = classpath:application-test.yml",
"spring.main.web-application-type = reactive"})
public class EncodeTransferMedataRestTemplateInterceptorTest { public class EncodeTransferMedataRestTemplateInterceptorTest {
@Autowired @Autowired
@ -66,8 +63,7 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
HttpHeaders httpHeaders = new HttpHeaders(); HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders); HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders);
String metadata = restTemplate String metadata = restTemplate
.exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET, .exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET, httpEntity, String.class)
httpEntity, String.class)
.getBody(); .getBody();
Assertions.assertThat(metadata).isEqualTo("2"); Assertions.assertThat(metadata).isEqualTo("2");
} }
@ -82,12 +78,8 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
} }
@RequestMapping("/test") @RequestMapping("/test")
public String test( public String test() throws UnsupportedEncodingException {
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr)
throws UnsupportedEncodingException {
return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"); return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
} }
} }
} }

@ -13,10 +13,9 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.metadata.core.filter; package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
@ -25,9 +24,7 @@ import java.util.Map;
import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.metadata.core.EncodeTransferMedataScgFilter;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@ -44,12 +41,15 @@ import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/** /**
* Test for {@link EncodeTransferMedataScgFilter}.
*
* @author quan * @author quan
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, @SpringBootTest(webEnvironment = RANDOM_PORT,
classes = EncodeTransferMedataScgFilterTest.TestApplication.class, classes = EncodeTransferMedataScgFilterTest.TestApplication.class,
properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) properties = {"spring.config.location = classpath:application-test.yml",
"spring.main.web-application-type = reactive"})
public class EncodeTransferMedataScgFilterTest { public class EncodeTransferMedataScgFilterTest {
@Autowired @Autowired
@ -61,14 +61,20 @@ public class EncodeTransferMedataScgFilterTest {
@Test @Test
public void testTransitiveMetadataFromApplicationConfig() throws UnsupportedEncodingException { public void testTransitiveMetadataFromApplicationConfig() throws UnsupportedEncodingException {
EncodeTransferMedataScgFilter filter = applicationContext.getBean(EncodeTransferMedataScgFilter.class); EncodeTransferMedataScgFilter filter = applicationContext.getBean(EncodeTransferMedataScgFilter.class);
// Mock Server Http Request
MockServerHttpRequest.BaseBuilder<?> builder = MockServerHttpRequest.get(""); MockServerHttpRequest.BaseBuilder<?> builder = MockServerHttpRequest.get("");
MockServerWebExchange exchange = MockServerWebExchange.from(builder); MockServerWebExchange exchange = MockServerWebExchange.from(builder);
filter.filter(exchange, chain); filter.filter(exchange, chain);
String metadataStr = exchange.getRequest().getHeaders().getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA);
String decode = URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name()); // Check metadata str
String metadata = exchange.getRequest().getHeaders().getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA);
Assertions.assertThat(metadata).isNotNull();
String decode = URLDecoder.decode(metadata, StandardCharsets.UTF_8.name());
Map<String, String> transitiveMap = JacksonUtils.deserialize2Map(decode); Map<String, String> transitiveMap = JacksonUtils.deserialize2Map(decode);
Assertions.assertThat(transitiveMap.size()).isEqualTo(1); Assertions.assertThat(transitiveMap.size()).isEqualTo(1);
Assert.assertEquals(transitiveMap.get("b"), "2"); Assertions.assertThat(transitiveMap.get("b")).isEqualTo("2");
} }
@SpringBootApplication @SpringBootApplication

@ -16,7 +16,7 @@
* *
*/ */
package com.tencent.cloud.metadata.core.filter; package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
@ -26,9 +26,7 @@ import java.util.Map;
import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.metadata.core.EncodeTransferMetadataZuulFilter;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -43,20 +41,21 @@ import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/** /**
* Test for {@link EncodeTransferMetadataZuulFilter}.
*
* @author quan * @author quan
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT, @SpringBootTest(webEnvironment = RANDOM_PORT,
classes = EncodeTransferMetadataZuulFilterTest.TestApplication.class, classes = EncodeTransferMetadataZuulFilterTest.TestApplication.class,
properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) properties = {"spring.config.location = classpath:application-test.yml",
"spring.main.web-application-type = reactive"})
public class EncodeTransferMetadataZuulFilterTest { public class EncodeTransferMetadataZuulFilterTest {
private final MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
@Autowired @Autowired
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
@Before @Before
public void init() { public void init() {
RequestContext ctx = RequestContext.getCurrentContext(); RequestContext ctx = RequestContext.getCurrentContext();
@ -70,11 +69,13 @@ public class EncodeTransferMetadataZuulFilterTest {
filter.run(); filter.run();
final RequestContext ctx = RequestContext.getCurrentContext(); final RequestContext ctx = RequestContext.getCurrentContext();
Map<String, String> zuulRequestHeaders = ctx.getZuulRequestHeaders(); Map<String, String> zuulRequestHeaders = ctx.getZuulRequestHeaders();
String metaData = zuulRequestHeaders.get(MetadataConstant.HeaderName.CUSTOM_METADATA.toLowerCase()); String metadata = zuulRequestHeaders.get(MetadataConstant.HeaderName.CUSTOM_METADATA.toLowerCase());
String decode = URLDecoder.decode(metaData, StandardCharsets.UTF_8.name()); Assertions.assertThat(metadata).isNotNull();
String decode = URLDecoder.decode(metadata, StandardCharsets.UTF_8.name());
Map<String, String> transitiveMap = JacksonUtils.deserialize2Map(decode); Map<String, String> transitiveMap = JacksonUtils.deserialize2Map(decode);
Assertions.assertThat(transitiveMap.size()).isEqualTo(1); Assertions.assertThat(transitiveMap.size()).isEqualTo(1);
Assert.assertEquals(transitiveMap.get("b"), "2"); Assertions.assertThat(transitiveMap.get("b")).isEqualTo("2");
} }
@SpringBootApplication @SpringBootApplication

@ -102,6 +102,12 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

@ -66,7 +66,5 @@ public class PolarisCircuitBreakerBootstrapConfiguration {
public int getOrder() { public int getOrder() {
return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER; return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER;
} }
} }
} }

@ -40,7 +40,8 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
* *
* @author Haotian Zhang * @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) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration") @ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration")
@AutoConfigureAfter(PolarisContextAutoConfiguration.class) @AutoConfigureAfter(PolarisContextAutoConfiguration.class)
@ -57,5 +58,4 @@ public class PolarisFeignClientAutoConfiguration {
public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) { public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) {
return new PolarisFeignBeanPostProcessor(consumerAPI); return new PolarisFeignBeanPostProcessor(consumerAPI);
} }
} }

@ -32,9 +32,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
/** /**
* @author : wh * Auto configuration PolarisRestTemplateAutoConfiguration .
* @date : 2022/6/21 21:34 *
* @description: Auto configuration PolarisRestTemplateAutoConfiguration * @author wh 2022/6/21
*/ */
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", @ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled",
havingValue = "true", matchIfMissing = true) havingValue = "true", matchIfMissing = true)
@ -44,13 +44,15 @@ public class PolarisRestTemplateAutoConfiguration {
@Bean @Bean
@ConditionalOnBean(RestTemplate.class) @ConditionalOnBean(RestTemplate.class)
public PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) { public PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler(
ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) {
return new PolarisRestTemplateResponseErrorHandler(consumerAPI, polarisResponseErrorHandler); return new PolarisRestTemplateResponseErrorHandler(consumerAPI, polarisResponseErrorHandler);
} }
@Bean @Bean
@ConditionalOnBean(RestTemplate.class) @ConditionalOnBean(RestTemplate.class)
public PolarisRestTemplateModifier polarisRestTemplateBeanPostProcessor(PolarisRestTemplateResponseErrorHandler restTemplateResponseErrorHandler) { public PolarisRestTemplateModifier polarisRestTemplateBeanPostProcessor(
PolarisRestTemplateResponseErrorHandler restTemplateResponseErrorHandler) {
return new PolarisRestTemplateModifier(restTemplateResponseErrorHandler); return new PolarisRestTemplateModifier(restTemplateResponseErrorHandler);
} }
} }

@ -55,7 +55,8 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
if (bean instanceof LoadBalancerFeignClient) { if (bean instanceof LoadBalancerFeignClient) {
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean); LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
return new PolarisLoadBalancerFeignClient( return new PolarisLoadBalancerFeignClient(
createPolarisFeignClient(client.getDelegate()), factory(), createPolarisFeignClient(client.getDelegate()),
factory(),
clientFactory()); clientFactory());
} }
if (bean instanceof FeignBlockingLoadBalancerClient) { if (bean instanceof FeignBlockingLoadBalancerClient) {
@ -91,5 +92,4 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
SpringClientFactory clientFactory() { SpringClientFactory clientFactory() {
return this.factory.getBean(SpringClientFactory.class); return this.factory.getBean(SpringClientFactory.class);
} }
} }

@ -32,5 +32,4 @@ public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBal
public PolarisFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) { public PolarisFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) {
super(delegate, loadBalancerClient); super(delegate, loadBalancerClient);
} }
} }

@ -43,7 +43,6 @@ import static feign.Util.checkNotNull;
*/ */
public class PolarisFeignClient implements Client { public class PolarisFeignClient implements Client {
private static final Logger LOG = LoggerFactory.getLogger(PolarisFeignClient.class); private static final Logger LOG = LoggerFactory.getLogger(PolarisFeignClient.class);
private final Client delegate; private final Client delegate;
@ -61,7 +60,7 @@ public class PolarisFeignClient implements Client {
try { try {
Response response = delegate.execute(request, options); Response response = delegate.execute(request, options);
// HTTP code greater than 500 is an exception // HTTP code greater than 500 is an exception
if (response.status() >= 500) { if (response.status() > 500) {
resultRequest.setRetStatus(RetStatus.RetFail); resultRequest.setRetStatus(RetStatus.RetFail);
} }
LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].", LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].",

@ -30,10 +30,8 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
*/ */
public class PolarisLoadBalancerFeignClient extends LoadBalancerFeignClient { public class PolarisLoadBalancerFeignClient extends LoadBalancerFeignClient {
public PolarisLoadBalancerFeignClient(Client delegate, public PolarisLoadBalancerFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) { SpringClientFactory clientFactory) {
super(delegate, lbClientFactory, clientFactory); super(delegate, lbClientFactory, clientFactory);
} }
} }

@ -20,9 +20,9 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.ResponseErrorHandler;
/** /**
* @author : wh * Polaris Response Error Handler Definition Of {@link ResponseErrorHandler}.
* @date : 2022/6/21 19:12 *
* @description: errorHandler {@link ResponseErrorHandler} * @author wh 2022/6/21
*/ */
public interface PolarisResponseErrorHandler extends ResponseErrorHandler { public interface PolarisResponseErrorHandler extends ResponseErrorHandler {

@ -28,16 +28,16 @@ import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
/** /**
* @author : wh * Auto configuration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced},
* @date : 2022/6/21 21:20 * then replace {@link org.springframework.web.client.ResponseErrorHandler}
* @description: auto configuration RestTemplate Find the RestTemplate bean annotated with {@link LoadBalanced} and replace {@link org.springframework.web.client.ResponseErrorHandler} * with {@link PolarisRestTemplateResponseErrorHandler} .
* with {@link PolarisRestTemplateResponseErrorHandler} *
* @author wh 2022/6/21
*/ */
public class PolarisRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton { public class PolarisRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton {
private ApplicationContext applicationContext;
private final PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler; private final PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler;
private ApplicationContext applicationContext;
public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) { public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) {
this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler; this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler;

@ -29,42 +29,44 @@ import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus; import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.rpc.ServiceCallResult;
import com.tencent.polaris.api.utils.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.ResponseErrorHandler;
/** /**
* @author : wh * Extend ResponseErrorHandler to get request information.
* @date : 2022/6/21 17:25 *
* @description: Extend ResponseErrorHandler to get request information * @author wh 2022/6/21
*/ */
public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHandler { public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHandler {
private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateResponseErrorHandler.class); private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateResponseErrorHandler.class);
private static final String FileName = "connection"; private static final String FIELD_NAME = "connection";
private final ConsumerAPI consumerAPI; private final ConsumerAPI consumerAPI;
private final PolarisResponseErrorHandler polarisResponseErrorHandler; private final PolarisResponseErrorHandler polarisResponseErrorHandler;
public PolarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI, PolarisResponseErrorHandler polarisResponseErrorHandler) { public PolarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI,
PolarisResponseErrorHandler polarisResponseErrorHandler) {
this.consumerAPI = consumerAPI; this.consumerAPI = consumerAPI;
this.polarisResponseErrorHandler = polarisResponseErrorHandler; this.polarisResponseErrorHandler = polarisResponseErrorHandler;
} }
@Override @Override
public boolean hasError(ClientHttpResponse response) { public boolean hasError(@NonNull ClientHttpResponse response) {
return true; return true;
} }
@Override @Override
public void handleError(ClientHttpResponse response) throws IOException { public void handleError(@NonNull ClientHttpResponse response) throws IOException {
if (Objects.nonNull(polarisResponseErrorHandler)) { if (Objects.nonNull(polarisResponseErrorHandler)) {
if (polarisResponseErrorHandler.hasError(response)) { if (polarisResponseErrorHandler.hasError(response)) {
polarisResponseErrorHandler.handleError(response); polarisResponseErrorHandler.handleError(response);
@ -72,12 +74,23 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
} }
} }
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { @Override
ServiceCallResult resultRequest = null; public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response)
throws IOException {
ServiceCallResult resultRequest = createServiceCallResult(url);
try { try {
resultRequest = builderServiceCallResult(url, response); HttpURLConnection connection = (HttpURLConnection) ReflectionUtils.getFieldValue(response, FIELD_NAME);
if (connection != null) {
URL realURL = connection.getURL();
resultRequest.setHost(realURL.getHost());
resultRequest.setPort(realURL.getPort());
}
if (response.getStatusCode().value() > 500) {
resultRequest.setRetStatus(RetStatus.RetFail);
}
} }
catch (IOException e) { catch (Exception e) {
LOG.error("Will report response of {} url {}", response, url, e); LOG.error("Will report response of {} url {}", response, url, e);
throw e; throw e;
} }
@ -86,7 +99,7 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
} }
} }
private ServiceCallResult builderServiceCallResult(URI uri, ClientHttpResponse response) throws IOException { private ServiceCallResult createServiceCallResult(URI uri) {
ServiceCallResult resultRequest = new ServiceCallResult(); ServiceCallResult resultRequest = new ServiceCallResult();
String serviceName = uri.getHost(); String serviceName = uri.getHost();
resultRequest.setService(serviceName); resultRequest.setService(serviceName);
@ -98,14 +111,6 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {
resultRequest.setCallerService(new ServiceKey(sourceNamespace, 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; return resultRequest;
} }
} }

@ -32,14 +32,14 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class PolarisCircuitBreakerBootstrapConfigurationTest { public class PolarisCircuitBreakerBootstrapConfigurationTest {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner() private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration( .withConfiguration(AutoConfigurations.of(PolarisCircuitBreakerBootstrapConfiguration.class))
AutoConfigurations.of(PolarisCircuitBreakerBootstrapConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test @Test
public void testDefaultInitialization() { public void testDefaultInitialization() {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(PolarisCircuitBreakerBootstrapConfiguration.CircuitBreakerConfigModifier.class); assertThat(context).hasSingleBean(
PolarisCircuitBreakerBootstrapConfiguration.CircuitBreakerConfigModifier.class);
}); });
} }
} }

@ -36,10 +36,9 @@ import static org.assertj.core.api.Assertions.assertThat;
public class PolarisFeignClientAutoConfigurationTest { public class PolarisFeignClientAutoConfigurationTest {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner() private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration( .withConfiguration(AutoConfigurations.of(
AutoConfigurations.of( PolarisContextAutoConfiguration.class,
PolarisContextAutoConfiguration.class, PolarisFeignClientAutoConfiguration.class))
PolarisFeignClientAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test @Test
@ -49,5 +48,4 @@ public class PolarisFeignClientAutoConfigurationTest {
assertThat(context).hasSingleBean(PolarisFeignBeanPostProcessor.class); assertThat(context).hasSingleBean(PolarisFeignBeanPostProcessor.class);
}); });
} }
} }

@ -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 com.tencent.cloud.polaris.circuitbreaker.config.PolarisRestTemplateAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateModifier;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test For {@link PolarisRestTemplateAutoConfiguration} .
*
* @author <a href="mailto:iskp.me@gmail.com">Palmer Xu</a> 2022-06-28
*/
public class PolarisRestTemplateAutoConfigurationTest {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
PolarisRestTemplateAutoConfigurationTester.class,
PolarisContextAutoConfiguration.class,
PolarisRestTemplateAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test
public void testInitialization() {
this.contextRunner
.run(context -> {
assertThat(context).hasSingleBean(PolarisRestTemplateModifier.class);
assertThat(context).hasSingleBean(PolarisRestTemplateResponseErrorHandler.class);
});
}
@Configuration
@EnableAutoConfiguration
@AutoConfigureBefore(PolarisRestTemplateAutoConfiguration.class)
static class PolarisRestTemplateAutoConfigurationTester {
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
}

@ -73,7 +73,8 @@ public class PolarisFeignClientTest {
} }
try { try {
assertThat(new PolarisFeignClient(mock(Client.class), mock(ConsumerAPI.class))).isInstanceOf(PolarisFeignClient.class); assertThat(new PolarisFeignClient(mock(Client.class), mock(ConsumerAPI.class)))
.isInstanceOf(PolarisFeignClient.class);
} }
catch (Throwable e) { catch (Throwable e) {
fail("Exception encountered.", e); fail("Exception encountered.", e);
@ -90,7 +91,7 @@ public class PolarisFeignClientTest {
return Response.builder().request(request).status(200).build(); return Response.builder().request(request).status(200).build();
} }
else if (request.httpMethod().equals(Request.HttpMethod.POST)) { else if (request.httpMethod().equals(Request.HttpMethod.POST)) {
return Response.builder().request(request).status(500).build(); return Response.builder().request(request).status(502).build();
} }
throw new IOException("Mock exception."); throw new IOException("Mock exception.");
}).when(delegate).execute(any(Request.class), nullable(Request.Options.class)); }).when(delegate).execute(any(Request.class), nullable(Request.Options.class));
@ -113,10 +114,10 @@ public class PolarisFeignClientTest {
Maps.newHashMap(), null, requestTemplate), null); Maps.newHashMap(), null, requestTemplate), null);
assertThat(response.status()).isEqualTo(200); assertThat(response.status()).isEqualTo(200);
// 200 // 502
response = polarisFeignClient.execute(Request.create(Request.HttpMethod.POST, "http://localhost:8080/test", response = polarisFeignClient.execute(Request.create(Request.HttpMethod.POST, "http://localhost:8080/test",
Maps.newHashMap(), null, requestTemplate), null); Maps.newHashMap(), null, requestTemplate), null);
assertThat(response.status()).isEqualTo(500); assertThat(response.status()).isEqualTo(502);
// Exception // Exception
try { try {

@ -15,14 +15,13 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker; package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler;
import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.core.ConsumerAPI;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -36,9 +35,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* @author : wh * Test For {@link PolarisRestTemplateResponseErrorHandler}.
* @date : 2022/6/22 09:00 *
* @description: Test for {@link PolarisRestTemplateResponseErrorHandler}. * @author wh 2022/6/22
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = PolarisRestTemplateResponseErrorHandlerTest.TestApplication.class, @SpringBootTest(classes = PolarisRestTemplateResponseErrorHandlerTest.TestApplication.class,

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker; package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -29,9 +29,9 @@ import org.springframework.util.StringUtils;
/** /**
* @author : wh * Mock Test for {@link AbstractClientHttpResponse}.
* @date : 2022/6/22 09:00 *
* @description: mock {@link org.springframework.http.client.SimpleClientHttpResponse} * @author wh 2022/6/22
*/ */
public class SimpleClientHttpResponseTest extends AbstractClientHttpResponse { public class SimpleClientHttpResponseTest extends AbstractClientHttpResponse {
@ -102,5 +102,4 @@ public class SimpleClientHttpResponseTest extends AbstractClientHttpResponse {
// ignore // ignore
} }
} }
} }

@ -71,6 +71,18 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId> <artifactId>mockito-inline</artifactId>

@ -31,7 +31,6 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
* Read configuration from spring cloud's configuration file and override polaris.yaml. * Read configuration from spring cloud's configuration file and override polaris.yaml.
* *

@ -40,8 +40,7 @@ import org.springframework.core.env.Environment;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled @ConditionalOnPolarisEnabled
@ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", @ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", matchIfMissing = true)
matchIfMissing = true)
@Import(PolarisContextAutoConfiguration.class) @Import(PolarisContextAutoConfiguration.class)
public class PolarisConfigBootstrapAutoConfiguration { public class PolarisConfigBootstrapAutoConfiguration {
@ -80,5 +79,4 @@ public class PolarisConfigBootstrapAutoConfiguration {
PolarisContextProperties polarisContextProperties) { PolarisContextProperties polarisContextProperties) {
return new ConfigurationModifier(polarisConfigProperties, polarisContextProperties); return new ConfigurationModifier(polarisConfigProperties, polarisContextProperties);
} }
} }

@ -52,8 +52,7 @@ import org.springframework.util.StringUtils;
@Order(0) @Order(0)
public class PolarisConfigFileLocator implements PropertySourceLocator { public class PolarisConfigFileLocator implements PropertySourceLocator {
private static final Logger LOGGER = LoggerFactory private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigFileLocator.class);
.getLogger(PolarisConfigFileLocator.class);
private static final String POLARIS_CONFIG_PROPERTY_SOURCE_NAME = "polaris-config"; private static final String POLARIS_CONFIG_PROPERTY_SOURCE_NAME = "polaris-config";
@ -115,7 +114,7 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
private List<ConfigFileMetadata> getInternalConfigFiles() { private List<ConfigFileMetadata> getInternalConfigFiles() {
String namespace = polarisContextProperties.getNamespace(); String namespace = polarisContextProperties.getNamespace();
String serviceName = polarisContextProperties.getService(); String serviceName = polarisContextProperties.getService();
if (StringUtils.isEmpty(serviceName)) { if (!StringUtils.hasText(serviceName)) {
serviceName = environment.getProperty("spring.application.name"); serviceName = environment.getProperty("spring.application.name");
} }
@ -125,7 +124,7 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
String[] activeProfiles = environment.getActiveProfiles(); String[] activeProfiles = environment.getActiveProfiles();
for (String activeProfile : activeProfiles) { for (String activeProfile : activeProfiles) {
if (StringUtils.isEmpty(activeProfile)) { if (!StringUtils.hasText(activeProfile)) {
continue; continue;
} }
@ -137,7 +136,7 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.yml")); internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.yml"));
for (String activeProfile : activeProfiles) { for (String activeProfile : activeProfiles) {
if (StringUtils.isEmpty(activeProfile)) { if (!StringUtils.hasText(activeProfile)) {
continue; continue;
} }
@ -160,9 +159,8 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
for (ConfigFileGroup configFileGroup : configFileGroups) { for (ConfigFileGroup configFileGroup : configFileGroups) {
String group = configFileGroup.getName(); String group = configFileGroup.getName();
if (StringUtils.isEmpty(group)) { if (!StringUtils.hasText(group)) {
throw new IllegalArgumentException( throw new IllegalArgumentException("polaris config group name cannot be empty.");
"polaris config group name cannot be empty.");
} }
List<String> files = configFileGroup.getFiles(); List<String> files = configFileGroup.getFiles();
@ -171,16 +169,14 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
} }
for (String fileName : files) { for (String fileName : files) {
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource( PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(namespace, group, fileName);
namespace, group, fileName);
compositePropertySource.addPropertySource(polarisPropertySource); compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource); polarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info( LOGGER.info("[SCT Config] Load and inject polaris config file success."
"[SCT Config] Load and inject polaris config file success. namespace = {}, group = {}, fileName = {}", + " namespace = {}, group = {}, fileName = {}", namespace, group, fileName);
namespace, group, fileName);
} }
} }
} }
@ -189,21 +185,18 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
String group, String fileName) { String group, String fileName) {
ConfigKVFile configKVFile; ConfigKVFile configKVFile;
// unknown extension is resolved as properties file // unknown extension is resolved as properties file
if (ConfigFileFormat.isPropertyFile(fileName) if (ConfigFileFormat.isPropertyFile(fileName) || ConfigFileFormat.isUnknownFile(fileName)) {
|| ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName); configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
} }
else if (ConfigFileFormat.isYamlFile(fileName)) { else if (ConfigFileFormat.isYamlFile(fileName)) {
configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName); configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName);
} }
else { else {
LOGGER.warn( LOGGER.warn("[SCT Config] Unsupported config file. namespace = {}, group = {}, fileName = {}",
"[SCT Config] Unsupported config file. namespace = {}, group = {}, fileName = {}",
namespace, group, fileName); namespace, group, fileName);
throw new IllegalStateException( throw new IllegalStateException("Only configuration files in the format of properties / yaml / yaml"
"Only configuration files in the format of properties / yaml / yaml" + " can be injected into the spring context");
+ " can be injected into the spring context");
} }
Map<String, Object> map = new ConcurrentHashMap<>(); Map<String, Object> map = new ConcurrentHashMap<>();
@ -213,5 +206,4 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
return new PolarisPropertySource(namespace, group, fileName, configKVFile, map); return new PolarisPropertySource(namespace, group, fileName, configKVFile, map);
} }
} }

@ -74,5 +74,4 @@ public class PolarisPropertySource extends MapPropertySource {
return "PolarisPropertySource{" + "namespace='" + namespace + '\'' + ", group='" return "PolarisPropertySource{" + "namespace='" + namespace + '\'' + ", group='"
+ group + '\'' + ", fileName='" + fileName + '\'' + '}'; + group + '\'' + ", fileName='" + fileName + '\'' + '}';
} }
} }

@ -53,8 +53,7 @@ import org.springframework.util.CollectionUtils;
public class PolarisPropertySourceAutoRefresher public class PolarisPropertySourceAutoRefresher
implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware { implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory private static final Logger LOGGER = LoggerFactory.getLogger(PolarisPropertySourceAutoRefresher.class);
.getLogger(PolarisPropertySourceAutoRefresher.class);
private final PolarisConfigProperties polarisConfigProperties; private final PolarisConfigProperties polarisConfigProperties;
@ -92,8 +91,7 @@ public class PolarisPropertySourceAutoRefresher
} }
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
throws BeansException {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); this.beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
this.typeConverter = this.beanFactory.getTypeConverter(); this.typeConverter = this.beanFactory.getTypeConverter();
@ -109,8 +107,7 @@ public class PolarisPropertySourceAutoRefresher
return; return;
} }
List<PolarisPropertySource> polarisPropertySources = polarisPropertySourceManager List<PolarisPropertySource> polarisPropertySources = polarisPropertySourceManager.getAllPropertySources();
.getAllPropertySources();
if (CollectionUtils.isEmpty(polarisPropertySources)) { if (CollectionUtils.isEmpty(polarisPropertySources)) {
return; return;
} }

@ -33,12 +33,10 @@ public class PolarisPropertySourceManager {
private final Map<String, PolarisPropertySource> polarisPropertySources = new ConcurrentHashMap<>(); private final Map<String, PolarisPropertySource> polarisPropertySources = new ConcurrentHashMap<>();
public void addPropertySource(PolarisPropertySource polarisPropertySource) { public void addPropertySource(PolarisPropertySource polarisPropertySource) {
polarisPropertySources.putIfAbsent(polarisPropertySource.getPropertySourceName(), polarisPropertySources.putIfAbsent(polarisPropertySource.getPropertySourceName(), polarisPropertySource);
polarisPropertySource);
} }
public List<PolarisPropertySource> getAllPropertySources() { public List<PolarisPropertySource> getAllPropertySources() {
return new ArrayList<>(polarisPropertySources.values()); return new ArrayList<>(polarisPropertySources.values());
} }
} }

@ -96,10 +96,8 @@ public class PolarisConfigAnnotationProcessor implements BeanPostProcessor, Prio
Set<String> interestedKeys = Set<String> interestedKeys =
annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null; annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null;
Set<String> interestedKeyPrefixes = Set<String> interestedKeyPrefixes =
annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes) annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes) : null;
: null;
addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes); addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes);
} }
} }

@ -38,7 +38,7 @@ public @interface PolarisConfigKVFileChangeListener {
/** /**
* The keys interested in the listener, will only be notified if any of the interested keys is changed. * The keys interested in the listener, will only be notified if any of the interested keys is changed.
* <br /> * <p>
* If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when any key 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 * @return interested keys in the listener
*/ */
@ -49,10 +49,9 @@ public @interface PolarisConfigKVFileChangeListener {
* The prefixes will simply be used to determine whether the {@code listener} should be notified or not using {@code changedKey.startsWith(prefix)}. * 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. * 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. * and "application" means that {@code listener} is interested in keys that starts with "application", such as "applicationName", "application.port", etc.
* <br /> * <p>
* If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when whatever key is changed. * 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 * @return interested key-prefixed in the listener
*/ */
String[] interestedKeyPrefixes() default {}; String[] interestedKeyPrefixes() default {};
} }

@ -56,5 +56,4 @@ public class ConfigFileGroup {
public String toString() { public String toString() {
return "ConfigFileGroup{" + "name='" + name + '\'' + ", file=" + files + '}'; return "ConfigFileGroup{" + "name='" + name + '\'' + ", file=" + files + '}';
} }
} }

@ -94,5 +94,4 @@ public class PolarisConfigProperties {
public void setGroups(List<ConfigFileGroup> groups) { public void setGroups(List<ConfigFileGroup> groups) {
this.groups = groups; this.groups = groups;
} }
} }

@ -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.endpoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
/**
* Endpoint of polaris config.
*
* @author shuiqingliu
**/
@Endpoint(id = "polaris-config")
public class PolarisConfigEndpoint {
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisPropertySourceManager polarisPropertySourceManager;
public PolarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisPropertySourceManager = polarisPropertySourceManager;
}
@ReadOperation
public Map<String, Object> polarisConfig() {
Map<String, Object> configInfo = new HashMap<>();
configInfo.put("PolarisConfigProperties", polarisConfigProperties);
List<PolarisPropertySource> propertySourceList = polarisPropertySourceManager.getAllPropertySources();
configInfo.put("PolarisPropertySource", propertySourceList);
return configInfo;
}
}

@ -0,0 +1,49 @@
/*
* 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.endpoint;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* The AutoConfiguration for Polaris Config's endpoint.
*
* @author shuiqingliu
**/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Endpoint.class)
@ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled",
matchIfMissing = true)
public class PolarisConfigEndpointAutoConfiguration {
@Bean
@ConditionalOnAvailableEndpoint
@ConditionalOnMissingBean
public PolarisConfigEndpoint polarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) {
return new PolarisConfigEndpoint(polarisConfigProperties, polarisPropertySourceManager);
}
}

@ -84,5 +84,4 @@ public final class ConfigChangeEvent {
public Set<String> interestedChangedKeys() { public Set<String> interestedChangedKeys() {
return interestedChangedKeys; return interestedChangedKeys;
} }
} }

@ -31,5 +31,4 @@ public interface ConfigChangeListener {
* @param changeEvent the event for this change * @param changeEvent the event for this change
*/ */
void onChange(ConfigChangeEvent changeEvent); void onChange(ConfigChangeEvent changeEvent);
} }

@ -107,5 +107,4 @@ public final class PolarisConfigChangeEventListener implements ApplicationListen
}); });
return ret; return ret;
} }
} }

@ -52,6 +52,7 @@ import static com.tencent.polaris.configuration.api.core.ChangeType.MODIFIED;
* <p>Refer to the Apollo project implementation * <p>Refer to the Apollo project implementation
* <code><a href=https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java> * <code><a href=https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java>
* AbstractConfig</a></code> * AbstractConfig</a></code>
*
* @author Palmer Xu 2022-06-06 * @author Palmer Xu 2022-06-06
*/ */
public final class PolarisConfigListenerContext { public final class PolarisConfigListenerContext {
@ -60,32 +61,30 @@ public final class PolarisConfigListenerContext {
* Logger instance. * Logger instance.
*/ */
private static final Logger LOG = LoggerFactory.getLogger(PolarisConfigListenerContext.class); private static final Logger LOG = LoggerFactory.getLogger(PolarisConfigListenerContext.class);
/** /**
* Execute service Atomic Reference Cache . * Execute service Atomic Reference Cache .
*/ */
private static final AtomicReference<ExecutorService> EAR = new AtomicReference<>(); private static final AtomicReference<ExecutorService> EAR = new AtomicReference<>();
/** /**
* All custom {@link ConfigChangeListener} instance defined in application . * All custom {@link ConfigChangeListener} instance defined in application .
*/ */
private static final List<ConfigChangeListener> listeners = Lists.newCopyOnWriteArrayList(); private static final List<ConfigChangeListener> listeners = Lists.newCopyOnWriteArrayList();
/** /**
* All custom interested keys defined in application . * All custom interested keys defined in application .
*/ */
private static final Map<ConfigChangeListener, Set<String>> interestedKeys = Maps.newHashMap(); private static final Map<ConfigChangeListener, Set<String>> interestedKeys = Maps.newHashMap();
/** /**
* All custom interested key prefixes defined in application . * All custom interested key prefixes defined in application .
*/ */
private static final Map<ConfigChangeListener, Set<String>> interestedKeyPrefixes = Maps.newHashMap(); private static final Map<ConfigChangeListener, Set<String>> interestedKeyPrefixes = Maps.newHashMap();
/** /**
* Cache all latest configuration information for users in the application environment . * Cache all latest configuration information for users in the application environment .
*/ */
private static final Cache<String, Object> properties = CacheBuilder.newBuilder().build(); private static final Cache<String, Object> properties = CacheBuilder.newBuilder().build();
private PolarisConfigListenerContext() {
}
/** /**
* Get or Created new execute server . * Get or Created new execute server .
* @return execute service instance of {@link ExecutorService} * @return execute service instance of {@link ExecutorService}
@ -273,5 +272,4 @@ public final class PolarisConfigListenerContext {
return Collections.unmodifiableSet(interestedChangedKeys); return Collections.unmodifiableSet(interestedChangedKeys);
} }
} }

@ -1,4 +1,5 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration,\
com.tencent.cloud.polaris.config.endpoint.PolarisConfigEndpointAutoConfiguration

@ -31,7 +31,8 @@ import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener;
/** /**
* Mock config kv file for test. * Mock config kv file for test.
*@author lepdou 2022-06-11 *
* @author lepdou 2022-06-11
*/ */
public class MockedConfigKVFile implements ConfigKVFile { public class MockedConfigKVFile implements ConfigKVFile {

@ -41,12 +41,15 @@ import org.springframework.core.env.PropertySource;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* test for {@link PolarisConfigFileLocator} * test for {@link PolarisConfigFileLocator}.
*@author lepdou 2022-06-11 *
* @author lepdou 2022-06-11
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class PolarisConfigFileLocatorTest { public class PolarisConfigFileLocatorTest {
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
@Mock @Mock
private PolarisConfigProperties polarisConfigProperties; private PolarisConfigProperties polarisConfigProperties;
@Mock @Mock
@ -58,9 +61,6 @@ public class PolarisConfigFileLocatorTest {
@Mock @Mock
private Environment environment; private Environment environment;
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
@Test @Test
public void testLoadApplicationPropertiesFile() { public void testLoadApplicationPropertiesFile() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties, PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,

@ -48,12 +48,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* test for {@link PolarisPropertySourceAutoRefresher} * test for {@link PolarisPropertySourceAutoRefresher}.
*@author lepdou 2022-06-11 *
* @author lepdou 2022-06-11
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class PolarisPropertiesSourceAutoRefresherTest { public class PolarisPropertiesSourceAutoRefresherTest {
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
private final String testFileName = "application.properties";
@Mock @Mock
private PolarisConfigProperties polarisConfigProperties; private PolarisConfigProperties polarisConfigProperties;
@Mock @Mock
@ -67,10 +71,6 @@ public class PolarisPropertiesSourceAutoRefresherTest {
@Mock @Mock
private PlaceholderHelper placeholderHelper; private PlaceholderHelper placeholderHelper;
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
private final String testFileName = "application.properties";
@Test @Test
public void testConfigFileChanged() throws Exception { public void testConfigFileChanged() throws Exception {
PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties, PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties,
@ -83,8 +83,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
when(applicationContext.getBeanFactory()).thenReturn(beanFactory); when(applicationContext.getBeanFactory()).thenReturn(beanFactory);
refresher.setApplicationContext(applicationContext); refresher.setApplicationContext(applicationContext);
when(typeConverter.convertIfNecessary(any(), any(), (Field) any())).thenReturn("v11"); when(typeConverter.convertIfNecessary(any(), any(), (Field) any())).thenReturn("v11");
Collection<SpringValue> springValues = new ArrayList<>(); Collection<SpringValue> springValues = new ArrayList<>();
MockedConfigChange mockedConfigChange = new MockedConfigChange(); MockedConfigChange mockedConfigChange = new MockedConfigChange();
mockedConfigChange.setK1("v1"); mockedConfigChange.setK1("v1");
@ -95,8 +94,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
when(springValueRegistry.get(any(), any())).thenReturn(springValues); when(springValueRegistry.get(any(), any())).thenReturn(springValues);
when(polarisConfigProperties.isAutoRefresh()).thenReturn(true); when(polarisConfigProperties.isAutoRefresh()).thenReturn(true);
Map<String, Object> content = new HashMap<>(); Map<String, Object> content = new HashMap<>();
content.put("k1", "v1"); content.put("k1", "v1");
content.put("k2", "v2"); content.put("k2", "v2");
@ -123,5 +121,4 @@ public class PolarisPropertiesSourceAutoRefresherTest {
Assert.assertEquals("v11", mockedConfigChange.getK1()); Assert.assertEquals("v11", mockedConfigChange.getK1());
} }
} }

@ -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.config.endpoint;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.Lists;
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.config.PolarisConfigProperties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Test for polaris config endpoint.
*
* @author shuiqingliu
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisConfigEndpointTest {
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
private final String testFileName = "application.properties";
@Mock
private PolarisConfigProperties polarisConfigProperties;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
@Test
public void testPolarisConfigEndpoint() {
Map<String, Object> content = new HashMap<>();
content.put("k1", "v1");
content.put("k2", "v2");
content.put("k3", "v3");
MockedConfigKVFile file = new MockedConfigKVFile(content);
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName,
file, content);
when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
PolarisConfigEndpoint endpoint = new PolarisConfigEndpoint(polarisConfigProperties, polarisPropertySourceManager);
Map<String, Object> info = endpoint.polarisConfig();
assertThat(polarisConfigProperties).isEqualTo(info.get("PolarisConfigProperties"));
assertThat(Lists.newArrayList(polarisPropertySource)).isEqualTo(info.get("PolarisPropertySource"));
}
}

@ -39,13 +39,13 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
/** /**
* Integration testing for change listener. * Integration testing for change listener.
*@author lepdou 2022-06-11 *
* @author lepdou 2022-06-11
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = DEFINED_PORT, @SpringBootTest(webEnvironment = DEFINED_PORT,
classes = ConfigChangeListenerTest.TestApplication.class, classes = ConfigChangeListenerTest.TestApplication.class,
properties = {"server.port=8081", properties = {"server.port=8081", "spring.config.location = classpath:application-test.yml"})
"spring.config.location = classpath:application-test.yml"})
public class ConfigChangeListenerTest { public class ConfigChangeListenerTest {
@Autowired @Autowired
@ -120,5 +120,4 @@ public class ConfigChangeListenerTest {
} }
} }
} }
} }

@ -93,6 +93,18 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>io.projectreactor</groupId> <groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId> <artifactId>reactor-test</artifactId>

@ -27,9 +27,9 @@ import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
/** /**
* Spring Cloud Tencent config Override polaris config. * Spring Cloud Tencent config Override polaris config.
* *
*@author lepdou 2022-04-24 * @author lepdou 2022-04-24
*/ */
public class DiscoveryConfigModifier implements PolarisConfigModifier { public class DiscoveryConfigModifier implements PolarisConfigModifier {

@ -56,15 +56,13 @@ public class DiscoveryPropertiesAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ProviderAPI polarisProvider(SDKContext polarisContext) public ProviderAPI polarisProvider(SDKContext polarisContext) throws PolarisException {
throws PolarisException {
return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext); return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext);
} }
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ConsumerAPI polarisConsumer(SDKContext polarisContext) public ConsumerAPI polarisConsumer(SDKContext polarisContext) throws PolarisException {
throws PolarisException {
return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext); return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext);
} }
@ -98,5 +96,4 @@ public class DiscoveryPropertiesAutoConfiguration {
public boolean isDiscoveryEnabled() { public boolean isDiscoveryEnabled() {
return discoveryEnabled; return discoveryEnabled;
} }
} }

@ -28,6 +28,8 @@ import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
/** /**
* Condition if Polaris discovery is enabled.
*
* @author Haotian Zhang, Andrew Shan, Jie Cheng * @author Haotian Zhang, Andrew Shan, Jie Cheng
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

@ -29,21 +29,17 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
public class DiscoveryEnabledCondition implements Condition { public class DiscoveryEnabledCondition implements Condition {
@Override @Override
public boolean matches(ConditionContext conditionContext, public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
AnnotatedTypeMetadata annotatedTypeMetadata) {
boolean isDiscoveryEnabled = Boolean boolean isDiscoveryEnabled = Boolean.parseBoolean(
.parseBoolean(conditionContext.getEnvironment() conditionContext.getEnvironment().getProperty("spring.cloud.polaris.discovery.enabled", "true"));
.getProperty("spring.cloud.polaris.discovery.enabled", "true"));
boolean isConsulDiscoveryEnabled = Boolean boolean isConsulDiscoveryEnabled = Boolean.parseBoolean(
.parseBoolean(conditionContext.getEnvironment() conditionContext.getEnvironment().getProperty("spring.cloud.consul.enabled", "false"))
.getProperty("spring.cloud.consul.enabled", "false"))
&& Boolean.parseBoolean(conditionContext.getEnvironment() && Boolean.parseBoolean(conditionContext.getEnvironment()
.getProperty("spring.cloud.consul.discovery.enabled", "true")); .getProperty("spring.cloud.consul.discovery.enabled", "true"));
isDiscoveryEnabled |= isConsulDiscoveryEnabled; isDiscoveryEnabled |= isConsulDiscoveryEnabled;
return isDiscoveryEnabled; return isDiscoveryEnabled;
} }
} }

@ -43,5 +43,4 @@ public class PolarisDiscoveryAutoConfiguration {
PolarisDiscoveryHandler polarisDiscoveryHandler) { PolarisDiscoveryHandler polarisDiscoveryHandler) {
return new PolarisServiceDiscovery(polarisDiscoveryHandler); return new PolarisServiceDiscovery(polarisDiscoveryHandler);
} }
} }

@ -54,5 +54,4 @@ public class PolarisDiscoveryClient implements DiscoveryClient {
public List<String> getServices() { public List<String> getServices() {
return polarisServiceDiscovery.getServices(); return polarisServiceDiscovery.getServices();
} }
} }

@ -33,15 +33,12 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnBlockingDiscoveryEnabled @ConditionalOnBlockingDiscoveryEnabled
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, @AutoConfigureBefore({SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class})
CommonsClientAutoConfiguration.class })
@AutoConfigureAfter(PolarisDiscoveryAutoConfiguration.class) @AutoConfigureAfter(PolarisDiscoveryAutoConfiguration.class)
public class PolarisDiscoveryClientConfiguration { public class PolarisDiscoveryClientConfiguration {
@Bean @Bean
public DiscoveryClient polarisDiscoveryClient( public DiscoveryClient polarisDiscoveryClient(PolarisServiceDiscovery polarisServiceDiscovery) {
PolarisServiceDiscovery polarisServiceDiscovery) {
return new PolarisDiscoveryClient(polarisServiceDiscovery); return new PolarisDiscoveryClient(polarisServiceDiscovery);
} }
} }

@ -53,6 +53,7 @@ public class PolarisDiscoveryHandler {
/** /**
* Get a list of healthy instances. * Get a list of healthy instances.
*
* @param service service name * @param service service name
* @return list of healthy instances * @return list of healthy instances
*/ */
@ -66,6 +67,7 @@ public class PolarisDiscoveryHandler {
/** /**
* Return all instances for the given service. * Return all instances for the given service.
*
* @param service serviceName * @param service serviceName
* @return list of instances * @return list of instances
*/ */
@ -87,6 +89,7 @@ public class PolarisDiscoveryHandler {
/** /**
* Return all service for given namespace. * Return all service for given namespace.
*
* @return service list * @return service list
*/ */
public ServicesResponse GetServices() { public ServicesResponse GetServices() {
@ -95,5 +98,4 @@ public class PolarisDiscoveryHandler {
request.setNamespace(namespace); request.setNamespace(namespace);
return polarisConsumer.getServices(request); return polarisConsumer.getServices(request);
} }
} }

@ -31,6 +31,8 @@ import com.tencent.polaris.api.rpc.InstancesResponse;
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.ServiceInstance;
/** /**
* Polaris service discovery service.
*
* @author Haotian Zhang, Andrew Shan, Jie Cheng * @author Haotian Zhang, Andrew Shan, Jie Cheng
*/ */
public class PolarisServiceDiscovery { public class PolarisServiceDiscovery {
@ -49,8 +51,7 @@ public class PolarisServiceDiscovery {
*/ */
public List<ServiceInstance> getInstances(String serviceId) throws PolarisException { public List<ServiceInstance> getInstances(String serviceId) throws PolarisException {
List<ServiceInstance> instances = new ArrayList<>(); List<ServiceInstance> instances = new ArrayList<>();
InstancesResponse filteredInstances = polarisDiscoveryHandler InstancesResponse filteredInstances = polarisDiscoveryHandler.getHealthyInstances(serviceId);
.getHealthyInstances(serviceId);
ServiceInstances serviceInstances = filteredInstances.toServiceInstances(); ServiceInstances serviceInstances = filteredInstances.toServiceInstances();
for (Instance instance : serviceInstances.getInstances()) { for (Instance instance : serviceInstances.getInstances()) {
instances.add(new PolarisServiceInstance(instance)); instances.add(new PolarisServiceInstance(instance));
@ -67,5 +68,4 @@ public class PolarisServiceDiscovery {
return polarisDiscoveryHandler.GetServices().getServices().stream() return polarisDiscoveryHandler.GetServices().getServices().stream()
.map(ServiceInfo::getService).collect(Collectors.toList()); .map(ServiceInfo::getService).collect(Collectors.toList());
} }
} }

@ -38,13 +38,11 @@ import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
*/ */
public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient { public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient {
private static final Logger log = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(PolarisReactiveDiscoveryClient.class);
.getLogger(PolarisReactiveDiscoveryClient.class);
private PolarisServiceDiscovery polarisServiceDiscovery; private final PolarisServiceDiscovery polarisServiceDiscovery;
public PolarisReactiveDiscoveryClient( public PolarisReactiveDiscoveryClient(PolarisServiceDiscovery polarisServiceDiscovery) {
PolarisServiceDiscovery polarisServiceDiscovery) {
this.polarisServiceDiscovery = polarisServiceDiscovery; this.polarisServiceDiscovery = polarisServiceDiscovery;
} }
@ -55,7 +53,6 @@ public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient {
@Override @Override
public Flux<ServiceInstance> getInstances(String serviceId) { public Flux<ServiceInstance> getInstances(String serviceId) {
return Mono.justOrEmpty(serviceId).flatMapMany(loadInstancesFromPolaris()) return Mono.justOrEmpty(serviceId).flatMapMany(loadInstancesFromPolaris())
.subscribeOn(Schedulers.boundedElastic()); .subscribeOn(Schedulers.boundedElastic());
} }
@ -66,7 +63,7 @@ public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient {
return Flux.fromIterable(polarisServiceDiscovery.getInstances(serviceId)); return Flux.fromIterable(polarisServiceDiscovery.getInstances(serviceId));
} }
catch (PolarisException e) { catch (PolarisException e) {
log.error("get service instance[{}] from polaris error!", serviceId, e); LOG.error("get service instance[{}] from polaris error!", serviceId, e);
return Flux.empty(); return Flux.empty();
} }
}; };
@ -79,10 +76,9 @@ public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient {
return Flux.fromIterable(polarisServiceDiscovery.getServices()); return Flux.fromIterable(polarisServiceDiscovery.getServices());
} }
catch (Exception e) { catch (Exception e) {
log.error("get services from polaris server fail,", e); LOG.error("get services from polaris server fail,", e);
return Flux.empty(); return Flux.empty();
} }
}).subscribeOn(Schedulers.boundedElastic()); }).subscribeOn(Schedulers.boundedElastic());
} }
} }

@ -47,5 +47,4 @@ public class PolarisReactiveDiscoveryClientConfiguration {
PolarisServiceDiscovery polarisServiceDiscovery) { PolarisServiceDiscovery polarisServiceDiscovery) {
return new PolarisReactiveDiscoveryClient(polarisServiceDiscovery); return new PolarisReactiveDiscoveryClient(polarisServiceDiscovery);
} }
} }

@ -25,6 +25,7 @@ import com.tencent.polaris.client.util.NamedThreadFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent; import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
@ -38,7 +39,8 @@ import static com.tencent.cloud.polaris.discovery.refresh.PolarisServiceStatusCh
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisRefreshApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent>, ApplicationEventPublisherAware { public class PolarisRefreshApplicationReadyEventListener
implements ApplicationListener<ApplicationReadyEvent>, ApplicationEventPublisherAware, DisposableBean {
private static final Logger LOG = LoggerFactory.getLogger(PolarisRefreshApplicationReadyEventListener.class); private static final Logger LOG = LoggerFactory.getLogger(PolarisRefreshApplicationReadyEventListener.class);
private static final int DELAY = 60; private static final int DELAY = 60;
@ -47,7 +49,8 @@ public class PolarisRefreshApplicationReadyEventListener implements ApplicationL
private final ScheduledExecutorService refreshExecutor; private final ScheduledExecutorService refreshExecutor;
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
public PolarisRefreshApplicationReadyEventListener(PolarisDiscoveryHandler polarisDiscoveryHandler, PolarisServiceStatusChangeListener polarisServiceStatusChangeListener) { public PolarisRefreshApplicationReadyEventListener(PolarisDiscoveryHandler polarisDiscoveryHandler,
PolarisServiceStatusChangeListener polarisServiceStatusChangeListener) {
this.polarisDiscoveryHandler = polarisDiscoveryHandler; this.polarisDiscoveryHandler = polarisDiscoveryHandler;
this.polarisServiceStatusChangeListener = polarisServiceStatusChangeListener; this.polarisServiceStatusChangeListener = polarisServiceStatusChangeListener;
this.refreshExecutor = Executors.newSingleThreadScheduledExecutor( this.refreshExecutor = Executors.newSingleThreadScheduledExecutor(
@ -83,4 +86,9 @@ public class PolarisRefreshApplicationReadyEventListener implements ApplicationL
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher; this.publisher = applicationEventPublisher;
} }
@Override
public void destroy() {
refreshExecutor.shutdown();
}
} }

@ -35,7 +35,8 @@ import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
* Change listener of Polaris service info. When service info is created or deleted, or, instance of service is from 0 to * Change listener of Polaris service info. When service info is created or deleted, or, instance of service is from
* 0 to many.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@ -78,8 +79,10 @@ public class PolarisServiceStatusChangeListener extends AbstractResourceEventLis
LOG.debug("receive service instances={} change event", svcEventKey); LOG.debug("receive service instances={} change event", svcEventKey);
ServiceInstancesByProto oldIns = (ServiceInstancesByProto) oldValue; ServiceInstancesByProto oldIns = (ServiceInstancesByProto) oldValue;
ServiceInstancesByProto newIns = (ServiceInstancesByProto) newValue; ServiceInstancesByProto newIns = (ServiceInstancesByProto) newValue;
if ((CollectionUtils.isEmpty(oldIns.getInstances()) && !CollectionUtils.isEmpty(newIns.getInstances())) || if ((CollectionUtils.isEmpty(oldIns.getInstances())
(!CollectionUtils.isEmpty(oldIns.getInstances()) && CollectionUtils.isEmpty(newIns.getInstances()))) { && !CollectionUtils.isEmpty(newIns.getInstances())) ||
(!CollectionUtils.isEmpty(oldIns.getInstances())
&& CollectionUtils.isEmpty(newIns.getInstances()))) {
LOG.info("Service status of {} is update.", newIns.getService()); LOG.info("Service status of {} is update.", newIns.getService());
// Trigger reload of gateway route cache. // Trigger reload of gateway route cache.

@ -0,0 +1,82 @@
/*
* 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.endpoint;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.rpc.InstancesResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.cloud.client.discovery.DiscoveryClient;
/**
* Endpoint of polaris discovery, include discovery properties and service instance.
*
* @author shuiqingliu
*/
@Endpoint(id = "polaris-discovery")
public class PolarisDiscoveryEndPoint {
private final PolarisDiscoveryProperties polarisDiscoveryProperties;
private final DiscoveryClient polarisDiscoveryClient;
private final PolarisDiscoveryHandler polarisDiscoveryHandler;
public PolarisDiscoveryEndPoint(PolarisDiscoveryProperties polarisDiscoveryProperties, DiscoveryClient polarisDiscoveryClient, PolarisDiscoveryHandler polarisDiscoveryHandler) {
this.polarisDiscoveryProperties = polarisDiscoveryProperties;
this.polarisDiscoveryClient = polarisDiscoveryClient;
this.polarisDiscoveryHandler = polarisDiscoveryHandler;
}
@ReadOperation
public Map<String, Object> polarisDiscovery(@Selector String serviceId) {
Map<String, Object> polarisDisConveryInfo = new HashMap<>();
polarisDisConveryInfo.put("PolarisDiscoveryProperties", polarisDiscoveryProperties);
List<ServiceInstances> serviceInstancesInfoList = new ArrayList<>();
if (StringUtils.isNotEmpty(serviceId)) {
ServiceInstances serviceInstances = getServiceInstances(serviceId);
serviceInstancesInfoList.add(serviceInstances);
polarisDisConveryInfo.put("ServiceInstances", serviceInstancesInfoList);
return polarisDisConveryInfo;
}
for (String service : polarisDiscoveryClient.getServices()) {
ServiceInstances serviceInstances = getServiceInstances(service);
serviceInstancesInfoList.add(serviceInstances);
}
polarisDisConveryInfo.put("ServiceInstances", serviceInstancesInfoList);
return polarisDisConveryInfo;
}
private ServiceInstances getServiceInstances(String serviceId) {
InstancesResponse instancesResponse = polarisDiscoveryHandler.getHealthyInstances(serviceId);
ServiceInstances serviceInstances = instancesResponse.toServiceInstances();
return serviceInstances;
}
}

@ -0,0 +1,49 @@
/*
* 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.endpoint;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* The AutoConfiguration for Polaris Discovery's Endpoint.
*
* @author shuiqingliu
**/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Endpoint.class)
@ConditionalOnPolarisDiscoveryEnabled
public class PolarisDiscoveryEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint
public PolarisDiscoveryEndPoint polarisDiscoveryEndPoint(PolarisDiscoveryProperties polarisDiscoveryProperties,
DiscoveryClient discoveryClient, PolarisDiscoveryHandler polarisDiscoveryHandler) {
return new PolarisDiscoveryEndPoint(polarisDiscoveryProperties, discoveryClient, polarisDiscoveryHandler);
}
}

@ -164,7 +164,5 @@ public class ConsulContextProperties {
public int getOrder() { public int getOrder() {
return ModifierOrder.LAST; return ModifierOrder.LAST;
} }
} }
} }

@ -27,6 +27,8 @@ import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
/** /**
* Condition if Polaris registry is enabled.
*
* @author Haotian Zhang, Andrew Shan, Jie Cheng * @author Haotian Zhang, Andrew Shan, Jie Cheng
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

@ -31,11 +31,9 @@ import org.springframework.util.StringUtils;
* *
* @author Haotian Zhang, Andrew Shan, Jie Cheng * @author Haotian Zhang, Andrew Shan, Jie Cheng
*/ */
public class PolarisAutoServiceRegistration public class PolarisAutoServiceRegistration extends AbstractAutoServiceRegistration<Registration> {
extends AbstractAutoServiceRegistration<Registration> {
private static final Logger log = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(PolarisAutoServiceRegistration.class);
.getLogger(PolarisAutoServiceRegistration.class);
private final PolarisRegistration registration; private final PolarisRegistration registration;
@ -62,7 +60,7 @@ public class PolarisAutoServiceRegistration
@Override @Override
protected void register() { protected void register() {
if (!this.registration.isRegisterEnabled()) { if (!this.registration.isRegisterEnabled()) {
log.debug("Registration disabled."); LOG.debug("Registration disabled.");
return; return;
} }
if (this.registration.getPort() <= 0) { if (this.registration.getPort() <= 0) {
@ -96,5 +94,4 @@ public class PolarisAutoServiceRegistration
String appName = registration.getPolarisProperties().getService(); String appName = registration.getPolarisProperties().getService();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName; return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
} }
} }

@ -51,10 +51,8 @@ public class PolarisRegistration implements Registration, ServiceInstance {
private final SDKContext polarisContext; private final SDKContext polarisContext;
private final StaticMetadataManager staticMetadataManager; private final StaticMetadataManager staticMetadataManager;
private Map<String, String> metadata;
private final String host; private final String host;
private Map<String, String> metadata;
public PolarisRegistration( public PolarisRegistration(
DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration, DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration,
@ -89,8 +87,7 @@ public class PolarisRegistration implements Registration, ServiceInstance {
@Override @Override
public boolean isSecure() { public boolean isSecure() {
return StringUtils.equalsIgnoreCase(polarisDiscoveryProperties.getProtocol(), return StringUtils.equalsIgnoreCase(polarisDiscoveryProperties.getProtocol(), "https");
"https");
} }
@Override @Override

@ -51,8 +51,7 @@ import static org.springframework.util.ReflectionUtils.rethrowRuntimeException;
*/ */
public class PolarisServiceRegistry implements ServiceRegistry<Registration> { public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
private static final Logger log = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(PolarisServiceRegistry.class);
.getLogger(PolarisServiceRegistry.class);
private static final int ttl = 5; private static final int ttl = 5;
@ -82,9 +81,8 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
@Override @Override
public void register(Registration registration) { public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) { if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for polaris client..."); LOG.warn("No service to register for polaris client...");
return; return;
} }
// Register instance. // Register instance.
@ -107,7 +105,7 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
try { try {
ProviderAPI providerClient = polarisDiscoveryHandler.getProviderAPI(); ProviderAPI providerClient = polarisDiscoveryHandler.getProviderAPI();
providerClient.register(instanceRegisterRequest); providerClient.register(instanceRegisterRequest);
log.info("polaris registry, {} {} {}:{} {} register finished", LOG.info("polaris registry, {} {} {}:{} {} register finished",
polarisDiscoveryProperties.getNamespace(), polarisDiscoveryProperties.getNamespace(),
registration.getServiceId(), registration.getHost(), registration.getServiceId(), registration.getHost(),
registration.getPort(), staticMetadataManager.getMergedStaticMetadata()); registration.getPort(), staticMetadataManager.getMergedStaticMetadata());
@ -120,19 +118,17 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
} }
} }
catch (Exception e) { catch (Exception e) {
log.error("polaris registry, {} register failed...{},", LOG.error("polaris registry, {} register failed...{},", registration.getServiceId(), registration, e);
registration.getServiceId(), registration, e);
rethrowRuntimeException(e); rethrowRuntimeException(e);
} }
} }
@Override @Override
public void deregister(Registration registration) { public void deregister(Registration registration) {
LOG.info("De-registering from Polaris Server now...");
log.info("De-registering from Polaris Server now...");
if (StringUtils.isEmpty(registration.getServiceId())) { if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No dom to de-register for polaris client..."); LOG.warn("No dom to de-register for polaris client...");
return; return;
} }
@ -148,15 +144,14 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
providerClient.deRegister(deRegisterRequest); providerClient.deRegister(deRegisterRequest);
} }
catch (Exception e) { catch (Exception e) {
log.error("ERR_POLARIS_DEREGISTER, de-register failed...{},", registration, LOG.error("ERR_POLARIS_DEREGISTER, de-register failed...{},", registration, e);
e);
} }
finally { finally {
if (null != heartbeatExecutor) { if (null != heartbeatExecutor) {
heartbeatExecutor.shutdown(); heartbeatExecutor.shutdown();
} }
} }
log.info("De-registration finished."); LOG.info("De-registration finished.");
} }
@Override @Override
@ -172,8 +167,7 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
@Override @Override
public Object getStatus(Registration registration) { public Object getStatus(Registration registration) {
String serviceName = registration.getServiceId(); String serviceName = registration.getServiceId();
InstancesResponse instancesResponse = polarisDiscoveryHandler InstancesResponse instancesResponse = polarisDiscoveryHandler.getInstances(serviceName);
.getInstances(serviceName);
Instance[] instances = instancesResponse.getInstances(); Instance[] instances = instancesResponse.getInstances();
if (null == instances || instances.length == 0) { if (null == instances || instances.length == 0) {
return null; return null;
@ -207,13 +201,10 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
} }
String healthCheckUrl = String.format("http://%s:%s%s", String healthCheckUrl = String.format("http://%s:%s%s",
heartbeatRequest.getHost(), heartbeatRequest.getPort(), heartbeatRequest.getHost(), heartbeatRequest.getPort(), healthCheckEndpoint);
healthCheckEndpoint);
if (!OkHttpUtil.get(healthCheckUrl, null)) { if (!OkHttpUtil.get(healthCheckUrl, null)) {
log.error( LOG.error("backend service health check failed. health check endpoint = {}", healthCheckEndpoint);
"backend service health check failed. health check endpoint = {}",
healthCheckEndpoint);
return; return;
} }
} }
@ -221,12 +212,11 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
polarisDiscoveryHandler.getProviderAPI().heartbeat(heartbeatRequest); polarisDiscoveryHandler.getProviderAPI().heartbeat(heartbeatRequest);
} }
catch (PolarisException e) { catch (PolarisException e) {
log.error("polaris heartbeat[{}]", e.getCode(), e); LOG.error("polaris heartbeat[{}]", e.getCode(), e);
} }
catch (Exception e) { catch (Exception e) {
log.error("polaris heartbeat runtime error", e); LOG.error("polaris heartbeat runtime error", e);
} }
}, ttl, ttl, TimeUnit.SECONDS); }, ttl, ttl, TimeUnit.SECONDS);
} }
} }

@ -43,8 +43,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties @EnableConfigurationProperties
@ConditionalOnPolarisRegisterEnabled @ConditionalOnPolarisRegisterEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
matchIfMissing = true)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, @AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class, AutoServiceRegistrationAutoConfiguration.class,
PolarisDiscoveryAutoConfiguration.class}) PolarisDiscoveryAutoConfiguration.class})
@ -73,7 +72,6 @@ public class PolarisServiceRegistryAutoConfiguration {
PolarisServiceRegistry registry, PolarisServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties, AutoServiceRegistrationProperties autoServiceRegistrationProperties,
PolarisRegistration registration) { PolarisRegistration registration) {
return new PolarisAutoServiceRegistration(registry, return new PolarisAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
autoServiceRegistrationProperties, registration);
} }
} }

@ -29,19 +29,16 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
public class RegisterEnabledCondition implements Condition { public class RegisterEnabledCondition implements Condition {
@Override @Override
public boolean matches(ConditionContext conditionContext, public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
AnnotatedTypeMetadata annotatedTypeMetadata) {
boolean isRegisterEnabled = Boolean.parseBoolean(conditionContext.getEnvironment() boolean isRegisterEnabled = Boolean.parseBoolean(conditionContext.getEnvironment()
.getProperty("spring.cloud.polaris.discovery.register", "true")); .getProperty("spring.cloud.polaris.discovery.register", "true"));
boolean isConsulRegisterEnabled = Boolean boolean isConsulRegisterEnabled = Boolean.parseBoolean(
.parseBoolean(conditionContext.getEnvironment() conditionContext.getEnvironment().getProperty("spring.cloud.consul.enabled", "false"))
.getProperty("spring.cloud.consul.enabled", "false"))
&& Boolean.parseBoolean(conditionContext.getEnvironment() && Boolean.parseBoolean(conditionContext.getEnvironment()
.getProperty("spring.cloud.consul.discovery.register", "true")); .getProperty("spring.cloud.consul.discovery.register", "true"));
isRegisterEnabled |= isConsulRegisterEnabled; isRegisterEnabled |= isConsulRegisterEnabled;
return isRegisterEnabled; return isRegisterEnabled;
} }
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save