hoxton 2.0.0.0 (#1458)

* fix:fix restTemplateCustomizer bean conflict causing service to fail to start properly.

* fix:fix NullPointerException when properties contain kv with null value.

* fix: memory not released while using wildcard api call with circuitbreaker (#1361)

* fix: memory not released while using wildcard api call with circuitbreaker enabled

* fix: change version 1.13.3-Hoxton.SR12-SNAPSHOT

* fix: update polaris verion to 1.15.9 release

* Update CHANGELOG.md

* release 1.13.3-Hoxton.SR12 (#1362)

* fix: memory not released while using wildcard api call with circuitbreaker enabled

* fix: change version 1.13.3-Hoxton.SR12-SNAPSHOT

* fix: update polaris verion to 1.15.9 release

* Update CHANGELOG.md

* release: 1.13.3-Hoxton.SR12

* fix: fix PolarisCircuitBreakerConfiguration not clear when gateway invoke by wildcard apis (#1392)

* fix: CHANGE VERSION TO 1.13.4-Hoxton.SR12-SNAPSHOT (#1407)

* fix: fix PolarisCircuitBreakerConfiguration not clear when gateway invoked by wildcard apis

* Update CHANGELOG.md

* fix: restore PolarisCircuitBreakerUtils and use ThreadPoolUtils.waitAndStopThreadPools instead

* fix: 修复命名不一致问题

* fix: CHANGE VERSION TO 1.13.4-Hoxton.SR12-SNAPSHOT

* Update polaris version to 1.15.10-SNAPSHOT

* fix:fix rate limit window update bug.

* fix: fix npe when feign.hystrix.enabled=false (#1436)

* feat: support 2.0.0 feature

* fix

* feat: support lossless config from console & support warmup (#1435)
feat:add admin http handler. (#1448)
feat:support concurrency rate limit. (#1454)

* fix

* add changelog

* fix

* fix changelog

* fix

* fix checkstyle

* fix ut

---------

Co-authored-by: 码匠君 <pointer_v@qq.com>
Co-authored-by: Haotian Zhang <928016560@qq.com>
Co-authored-by: andrew shan <45474304+andrewshan@users.noreply.github.com>
Co-authored-by: shedfreewu <shedfreewu@tencent.com>
pull/1466/head
shedfreewu 10 months ago committed by GitHub
parent eab382b371
commit 84a98de136
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -16,3 +16,4 @@
- [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1306)
- [fix:fix no registry when lossless is disabled.](https://github.com/Tencent/spring-cloud-tencent/pull/1313)
- [fix: memory not released while using wildcard api call with circuitbreaker enabled](https://github.com/Tencent/spring-cloud-tencent/pull/1335)
- [feat: support 2.0.0](https://github.com/Tencent/spring-cloud-tencent/pull/1458)

@ -90,7 +90,7 @@
<properties>
<!-- Project revision -->
<revision>1.14.0-Hoxton.SR12-RC3</revision>
<revision>2.0.0.0-Hoxton.SR12-SNAPSHOT</revision>
<!-- Spring Framework -->
<spring.framework.version>5.2.25.RELEASE</spring.framework.version>

@ -49,6 +49,11 @@
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-contract</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-trace-plugin</artifactId>
</dependency>
</dependencies>
<build>

@ -26,7 +26,6 @@ import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedP
import com.tencent.cloud.metadata.core.EncodeTransferMedataScgEnhancedPlugin;
import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin;
import com.tencent.cloud.metadata.core.EncodeTransferMetadataZuulEnhancedPlugin;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -67,8 +66,8 @@ public class MetadataTransferAutoConfiguration {
}
@Bean
public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisContextProperties polarisContextProperties) {
return new DecodeTransferMetadataServletFilter(polarisContextProperties);
public DecodeTransferMetadataServletFilter metadataServletFilter() {
return new DecodeTransferMetadataServletFilter();
}
}
@ -80,8 +79,8 @@ public class MetadataTransferAutoConfiguration {
protected static class MetadataReactiveFilterConfig {
@Bean
public DecodeTransferMetadataReactiveFilter metadataReactiveFilter(PolarisContextProperties polarisContextProperties) {
return new DecodeTransferMetadataReactiveFilter(polarisContextProperties);
public DecodeTransferMetadataReactiveFilter metadataReactiveFilter() {
return new DecodeTransferMetadataReactiveFilter();
}
}
@ -91,11 +90,10 @@ public class MetadataTransferAutoConfiguration {
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "com.netflix.zuul.http.ZuulServlet")
@ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true)
protected static class MetadataTransferZuulFilterConfig {
@Bean
public EncodeTransferMetadataZuulEnhancedPlugin encodeTransferMedataZuulEnhancedPlugin() {
public EncodeTransferMetadataZuulEnhancedPlugin encodeTransferMetadataZuulEnhancedPlugin() {
return new EncodeTransferMetadataZuulEnhancedPlugin();
}

@ -27,7 +27,7 @@ import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.metadata.provider.ReactiveMetadataProvider;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
@ -39,8 +39,10 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static com.tencent.polaris.metadata.core.constant.MetadataConstants.LOCAL_IP;
/**
* Filter used for storing the metadata from upstream temporarily when web application is
@ -50,14 +52,8 @@ import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUST
*/
public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered {
private PolarisContextProperties polarisContextProperties;
private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataReactiveFilter.class);
public DecodeTransferMetadataReactiveFilter(PolarisContextProperties polarisContextProperties) {
this.polarisContextProperties = polarisContextProperties;
}
@Override
public int getOrder() {
return OrderConstant.Server.Reactive.DECODE_TRANSFER_METADATA_FILTER_ORDER;
@ -67,16 +63,34 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
// Get metadata string from http header.
ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest();
Map<String, String> internalTransitiveMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_METADATA);
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
// transitive metadata
// from specific header
Map<String, String> internalTransitiveMetadata = getInternalMetadata(serverHttpRequest, CUSTOM_METADATA);
// from header with specific prefix
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
Map<String, String> internalDisposableMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA);
// disposable metadata
// from specific header
Map<String, String> internalDisposableMetadata = getInternalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA);
Map<String, String> mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
ReactiveMetadataProvider metadataProvider = new ReactiveMetadataProvider(serverHttpRequest, polarisContextProperties.getLocalIpAddress());
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider);
// application metadata
Map<String, String> internalApplicationMetadata = getInternalMetadata(serverHttpRequest, APPLICATION_METADATA);
Map<String, String> mergedApplicationMetadata = new HashMap<>(internalApplicationMetadata);
String callerIp = "";
if (StringUtils.isNotBlank(mergedApplicationMetadata.get(LOCAL_IP))) {
callerIp = mergedApplicationMetadata.get(LOCAL_IP);
}
// message metadata
ReactiveMetadataProvider callerMessageMetadataProvider = new ReactiveMetadataProvider(serverHttpRequest, callerIp);
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, mergedApplicationMetadata, callerMessageMetadataProvider);
// Save to ServerWebExchange.
serverWebExchange.getAttributes().put(
MetadataConstant.HeaderName.METADATA_CONTEXT,
@ -89,7 +103,7 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered
.doFinally((type) -> MetadataContextHolder.remove());
}
private Map<String, String> getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
private Map<String, String> getInternalMetadata(ServerHttpRequest serverHttpRequest, String headerName) {
HttpHeaders httpHeaders = serverHttpRequest.getHeaders();
String customMetadataStr = UrlUtils.decode(httpHeaders.getFirst(headerName));
LOG.debug("Get upstream metadata string: {}", customMetadataStr);

@ -32,7 +32,7 @@ import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.metadata.provider.ServletMetadataProvider;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,8 +40,10 @@ import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static com.tencent.polaris.metadata.core.constant.MetadataConstants.LOCAL_IP;
/**
* Filter used for storing the metadata from upstream temporarily when web application is
@ -54,26 +56,36 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class);
private PolarisContextProperties polarisContextProperties;
public DecodeTransferMetadataServletFilter(PolarisContextProperties polarisContextProperties) {
this.polarisContextProperties = polarisContextProperties;
}
@Override
protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest,
@NonNull HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
// transitive metadata
// from specific header
Map<String, String> internalTransitiveMetadata = getInternalMetadata(httpServletRequest, CUSTOM_METADATA);
// from header with specific prefix
Map<String, String> customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(httpServletRequest);
Map<String, String> mergedTransitiveMetadata = new HashMap<>();
mergedTransitiveMetadata.putAll(internalTransitiveMetadata);
mergedTransitiveMetadata.putAll(customTransitiveMetadata);
// disposable metadata
// from specific header
Map<String, String> internalDisposableMetadata = getInternalMetadata(httpServletRequest, CUSTOM_DISPOSABLE_METADATA);
Map<String, String> mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata);
ServletMetadataProvider metadataProvider = new ServletMetadataProvider(httpServletRequest, polarisContextProperties.getLocalIpAddress());
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider);
// application metadata
Map<String, String> internalApplicationMetadata = getInternalMetadata(httpServletRequest, APPLICATION_METADATA);
Map<String, String> mergedApplicationMetadata = new HashMap<>(internalApplicationMetadata);
String callerIp = "";
if (StringUtils.isNotBlank(mergedApplicationMetadata.get(LOCAL_IP))) {
callerIp = mergedApplicationMetadata.get(LOCAL_IP);
}
// message metadata
ServletMetadataProvider callerMessageMetadataProvider = new ServletMetadataProvider(httpServletRequest, callerIp);
MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, mergedApplicationMetadata, callerMessageMetadataProvider);
TransHeadersTransfer.transfer(httpServletRequest);
try {

@ -39,6 +39,7 @@ import feign.Request;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -64,6 +65,7 @@ public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin {
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
@ -77,6 +79,9 @@ public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin {
// process custom metadata
this.buildMetadataHeader(request, customMetadata, CUSTOM_METADATA);
// add application metadata
this.buildMetadataHeader(request, applicationMetadata, APPLICATION_METADATA);
// set headers that need to be transmitted from the upstream
this.buildTransmittedHeader(request, transHeaders);
}

@ -35,6 +35,7 @@ import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.http.HttpRequest;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -60,6 +61,7 @@ public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedP
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
@ -72,6 +74,9 @@ public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedP
// build custom metadata request header
this.buildMetadataHeader(httpRequest, customMetadata, CUSTOM_METADATA);
// build application metadata request header
this.buildMetadataHeader(httpRequest, applicationMetadata, APPLICATION_METADATA);
// set headers that need to be transmitted from the upstream
this.buildTransmittedHeader(httpRequest, transHeaders);
}

@ -37,6 +37,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -69,6 +70,7 @@ public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin {
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
@ -77,6 +79,7 @@ public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin {
this.buildMetadataHeader(builder, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(builder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
this.buildMetadataHeader(builder, applicationMetadata, APPLICATION_METADATA);
TransHeadersTransfer.transfer(exchange.getRequest());
context.setOriginRequest(exchange.mutate().request(builder.build()).build());

@ -35,6 +35,7 @@ import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.client.ClientRequest;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
@ -59,6 +60,7 @@ public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlug
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
@ -70,6 +72,7 @@ public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlug
this.buildMetadataHeader(requestBuilder, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(requestBuilder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
this.buildMetadataHeader(requestBuilder, applicationMetadata, APPLICATION_METADATA);
this.buildTransmittedHeader(requestBuilder, transHeaders);
context.setOriginRequest(requestBuilder.build());

@ -18,9 +18,10 @@
package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
@ -35,15 +36,14 @@ import com.tencent.polaris.metadata.core.MetadataType;
import org.springframework.util.CollectionUtils;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.APPLICATION_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
/**
* Pre EnhancedPlugin for zuul to encode transfer metadata.
*
* @author Shedfree Wu
*/
public class EncodeTransferMetadataZuulEnhancedPlugin implements EnhancedPlugin {
@Override
public EnhancedPluginType getType() {
return EnhancedPluginType.Client.PRE;
@ -54,6 +54,8 @@ public class EncodeTransferMetadataZuulEnhancedPlugin implements EnhancedPlugin
if (!(context.getOriginRequest() instanceof RequestContext)) {
return;
}
// get request context
RequestContext requestContext = (RequestContext) context.getOriginRequest();
// get metadata of current thread
@ -61,21 +63,26 @@ public class EncodeTransferMetadataZuulEnhancedPlugin implements EnhancedPlugin
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> applicationMetadata = metadataContext.getApplicationMetadata();
MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false);
Map<String, String> calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders();
// currently only support transitive header from calleeMessageMetadataContainer
this.buildHeaderMap(requestContext, calleeTransitiveHeaders);
// Rebuild Metadata Header
this.buildMetadataHeader(requestContext, customMetadata, CUSTOM_METADATA);
this.buildMetadataHeader(requestContext, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);
this.buildMetadataHeader(requestContext, applicationMetadata, APPLICATION_METADATA);
TransHeadersTransfer.transfer(requestContext.getRequest());
}
private void buildHeaderMap(RequestContext context, Map<String, String> headerMap) {
private void buildHeaderMap(RequestContext requestContext, Map<String, String> headerMap) {
if (!CollectionUtils.isEmpty(headerMap)) {
headerMap.forEach((key, value) -> context.addZuulRequestHeader(key, UrlUtils.encode(value)));
headerMap.forEach((key, value) -> requestContext.addZuulRequestHeader(key, UrlUtils.encode(value)));
}
}
@ -88,7 +95,13 @@ public class EncodeTransferMetadataZuulEnhancedPlugin implements EnhancedPlugin
*/
private void buildMetadataHeader(RequestContext context, Map<String, String> metadata, String headerName) {
if (!CollectionUtils.isEmpty(metadata)) {
buildHeaderMap(context, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata)));
String encodedMetadata = JacksonUtils.serialize2Json(metadata);
try {
context.addZuulRequestHeader(headerName, URLEncoder.encode(encodedMetadata, UTF_8));
}
catch (UnsupportedEncodingException e) {
context.addZuulRequestHeader(headerName, encodedMetadata);
}
}
}

@ -0,0 +1,75 @@
/*
* 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.metadata.provider;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataProvider;
import com.tencent.polaris.metadata.core.constant.MetadataConstants;
import com.tencent.polaris.metadata.core.manager.CalleeMetadataContainerGroup;
import feign.RequestTemplate;
/**
* MetadataProvider used for Feign RequestTemplate.
*
* @author Haotian Zhang
*/
public class FeignRequestTemplateMetadataProvider implements MetadataProvider {
private final RequestTemplate requestTemplate;
public FeignRequestTemplateMetadataProvider(RequestTemplate requestTemplate) {
this.requestTemplate = requestTemplate;
}
@Override
public String getRawMetadataStringValue(String key) {
switch (key) {
case MessageMetadataContainer.LABEL_KEY_METHOD:
return requestTemplate.method();
case MessageMetadataContainer.LABEL_KEY_PATH:
URI uri = URI.create(requestTemplate.request().url());
return UrlUtils.decode(uri.getPath());
case MessageMetadataContainer.LABEL_KEY_CALLER_IP:
return CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.getRawMetadataStringValue(MetadataConstants.LOCAL_IP);
default:
return null;
}
}
@Override
public String getRawMetadataMapValue(String key, String mapKey) {
Map<String, Collection<String>> headers = requestTemplate.headers();
switch (key) {
case MessageMetadataContainer.LABEL_MAP_KEY_HEADER:
return UrlUtils.decode(ExpressionLabelUtils.getFirstValue(headers, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE:
return UrlUtils.decode(ExpressionLabelUtils.getCookieFirstValue(headers, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_QUERY:
return UrlUtils.decode(ExpressionLabelUtils.getFirstValue(requestTemplate.queries(), mapKey));
default:
return null;
}
}
}

@ -45,7 +45,7 @@ public class ReactiveMetadataProvider implements MetadataProvider {
public String getRawMetadataStringValue(String key) {
switch (key) {
case MessageMetadataContainer.LABEL_KEY_METHOD:
return serverHttpRequest.getMethodValue();
return serverHttpRequest.getMethod().name();
case MessageMetadataContainer.LABEL_KEY_PATH:
return UrlUtils.decode(serverHttpRequest.getPath().toString());
case MessageMetadataContainer.LABEL_KEY_CALLER_IP:

@ -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.metadata.provider;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataProvider;
import com.tencent.polaris.metadata.core.constant.MetadataConstants;
import com.tencent.polaris.metadata.core.manager.CalleeMetadataContainerGroup;
import org.springframework.http.HttpRequest;
/**
* MetadataProvider used for RestTemplate HttpRequest.
*
* @author Haotian Zhang
*/
public class RestTemplateMetadataProvider implements MetadataProvider {
private final HttpRequest request;
public RestTemplateMetadataProvider(HttpRequest request) {
this.request = request;
}
@Override
public String getRawMetadataStringValue(String key) {
switch (key) {
case MessageMetadataContainer.LABEL_KEY_METHOD:
return request.getMethod().toString();
case MessageMetadataContainer.LABEL_KEY_PATH:
return UrlUtils.decode(request.getURI().getPath());
case MessageMetadataContainer.LABEL_KEY_CALLER_IP:
return CalleeMetadataContainerGroup.getStaticApplicationMetadataContainer()
.getRawMetadataStringValue(MetadataConstants.LOCAL_IP);
default:
return null;
}
}
@Override
public String getRawMetadataMapValue(String key, String mapKey) {
switch (key) {
case MessageMetadataContainer.LABEL_MAP_KEY_HEADER:
return UrlUtils.decode(SpringWebExpressionLabelUtils.getHeaderValue(request, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE:
return UrlUtils.decode(SpringWebExpressionLabelUtils.getCookieValue(request, mapKey));
case MessageMetadataContainer.LABEL_MAP_KEY_QUERY:
return UrlUtils.decode(SpringWebExpressionLabelUtils.getQueryValue(request, mapKey));
default:
return null;
}
}
}

@ -17,7 +17,6 @@
*/
package com.tencent.cloud.metadata.provider;
import javax.servlet.http.HttpServletRequest;
import com.tencent.cloud.common.util.UrlUtils;
@ -26,6 +25,7 @@ import com.tencent.cloud.common.util.expresstion.ServletExpressionLabelUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import com.tencent.polaris.metadata.core.MetadataProvider;
/**
* MetadataProvider used for Servlet.
*

@ -20,7 +20,6 @@ package com.tencent.cloud.metadata.core;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -55,7 +54,7 @@ public class DecodeTransferMetadataReactiveFilterTest {
@BeforeEach
public void setUp() {
this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(new PolarisContextProperties());
this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter();
}
@Test

@ -1,106 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.metadata.core;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.rpc.enhancement.zuul.EnhancedPreZuulFilter;
import org.assertj.core.api.Assertions;
import org.assertj.core.util.Maps;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static com.tencent.cloud.common.constant.ContextConstant.UTF_8;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA;
import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY;
/**
* Test for {@link EncodeTransferMetadataZuulEnhancedPlugin}.
*
* @author quan, Shedfree Wu
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = EncodeTransferMetadataZuulFilterTest.TestApplication.class,
properties = {"spring.config.location = classpath:application-test.yml",
"spring.main.web-application-type = reactive"})
public class EncodeTransferMetadataZuulFilterTest {
private final MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
@Autowired
private ApplicationContext applicationContext;
@BeforeEach
void setUp() {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.clear();
ctx.setRequest(this.request);
}
@Test
public void testRun() throws ZuulException, UnsupportedEncodingException {
EnhancedPreZuulFilter filter = applicationContext.getBean(EnhancedPreZuulFilter.class);
RequestContext context = RequestContext.getCurrentContext();
context.set(SERVICE_ID_KEY, "test-service");
MetadataContext metadataContext = MetadataContextHolder.get();
metadataContext.setTransitiveMetadata(Maps.newHashMap("t-key", "t-value"));
metadataContext.setDisposableMetadata(Maps.newHashMap("d-key", "d-value"));
filter.run();
final RequestContext ctx = RequestContext.getCurrentContext();
Map<String, String> zuulRequestHeaders = ctx.getZuulRequestHeaders();
// convert header to lower case in com.netflix.zuul.context.RequestContext.addZuulRequestHeader
assertThat(zuulRequestHeaders.get(CUSTOM_METADATA.toLowerCase())).isNotNull();
assertThat(zuulRequestHeaders.get(CUSTOM_DISPOSABLE_METADATA.toLowerCase())).isNotNull();
String metadata = zuulRequestHeaders.get(CUSTOM_METADATA.toLowerCase());
Assertions.assertThat(metadata).isNotNull();
String decode = URLDecoder.decode(metadata, UTF_8);
Map<String, String> transitiveMap = JacksonUtils.deserialize2Map(decode);
// expect {"b":"2","t-key":"t-value"}
Assertions.assertThat(transitiveMap.size()).isEqualTo(2);
Assertions.assertThat(transitiveMap.get("b")).isEqualTo("2");
}
@SpringBootApplication
protected static class TestApplication {
}
}

@ -20,7 +20,7 @@ package com.tencent.cloud.metadata.provider;
import com.tencent.cloud.common.util.UrlUtils;
import com.tencent.polaris.metadata.core.MessageMetadataContainer;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpMethod;

@ -26,7 +26,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
@ -64,6 +63,10 @@
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-namespace</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>

@ -44,15 +44,20 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
public class PolarisCircuitBreaker implements CircuitBreaker, InvokeHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCircuitBreaker.class);
private final FunctionalDecorator decorator;
private final PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf;
private final ConsumerAPI consumerAPI;
private final FunctionalDecorator decorator;
private final InvokeHandler invokeHandler;
public PolarisCircuitBreaker(PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
FunctionalDecoratorRequest makeDecoratorRequest = new FunctionalDecoratorRequest(new ServiceKey(conf.getNamespace(), conf.getService()), conf.getMethod());
FunctionalDecoratorRequest makeDecoratorRequest = new FunctionalDecoratorRequest(
new ServiceKey(conf.getNamespace(), conf.getService()), conf.getProtocol(), conf.getMethod(), conf.getPath());
makeDecoratorRequest.setSourceService(new ServiceKey(conf.getSourceNamespace(), conf.getSourceService()));
makeDecoratorRequest.setResultToErrorCode(new PolarisResultToErrorCode());
this.consumerAPI = consumerAPI;

@ -17,13 +17,21 @@
package com.tencent.cloud.polaris.circuitbreaker;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties;
import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.utils.ThreadPoolUtils;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.util.NamedThreadFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
@ -33,7 +41,14 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
* @author seanyu 2023-02-27
*/
public class PolarisCircuitBreakerFactory
extends CircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> {
extends CircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> implements DisposableBean {
private final CircuitBreakAPI circuitBreakAPI;
private final ConsumerAPI consumerAPI;
private final ScheduledExecutorService cleanupService = Executors.newSingleThreadScheduledExecutor(
new NamedThreadFactory("sct-circuitbreaker-cleanup", true));
private Function<String, PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration> defaultConfiguration =
id -> {
@ -41,18 +56,22 @@ public class PolarisCircuitBreakerFactory
return new PolarisCircuitBreakerConfigBuilder()
.namespace(metadata[0])
.service(metadata[1])
.method(metadata[2])
.path(metadata[2])
.protocol(metadata[3])
.method(metadata[4])
.build();
};
private final CircuitBreakAPI circuitBreakAPI;
private final ConsumerAPI consumerAPI;
public PolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) {
public PolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
this.circuitBreakAPI = circuitBreakAPI;
this.consumerAPI = consumerAPI;
cleanupService.scheduleWithFixedDelay(
() -> {
getConfigurations().clear();
},
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(),
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(), TimeUnit.MILLISECONDS);
}
@Override
@ -65,7 +84,7 @@ public class PolarisCircuitBreakerFactory
@Override
protected PolarisCircuitBreakerConfigBuilder configBuilder(String id) {
String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id);
return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2]);
return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2], metadata[3], metadata[4]);
}
@Override
@ -73,4 +92,9 @@ public class PolarisCircuitBreakerFactory
this.defaultConfiguration = defaultConfiguration;
}
@Override
public void destroy() {
ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[] {cleanupService});
}
}

@ -51,7 +51,8 @@ public class ReactivePolarisCircuitBreaker implements ReactiveCircuitBreaker {
public ReactivePolarisCircuitBreaker(PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf,
ConsumerAPI consumerAPI,
CircuitBreakAPI circuitBreakAPI) {
InvokeContext.RequestContext requestContext = new FunctionalDecoratorRequest(new ServiceKey(conf.getNamespace(), conf.getService()), conf.getMethod());
InvokeContext.RequestContext requestContext = new FunctionalDecoratorRequest(
new ServiceKey(conf.getNamespace(), conf.getService()), conf.getProtocol(), conf.getMethod(), conf.getPath());
requestContext.setSourceService(new ServiceKey(conf.getSourceNamespace(), conf.getSourceService()));
requestContext.setResultToErrorCode(new PolarisResultToErrorCode());
this.consumerAPI = consumerAPI;

@ -17,13 +17,21 @@
package com.tencent.cloud.polaris.circuitbreaker;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties;
import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.utils.ThreadPoolUtils;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.util.NamedThreadFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
@ -33,7 +41,14 @@ import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFac
* @author seanyu 2023-02-27
*/
public class ReactivePolarisCircuitBreakerFactory extends
ReactiveCircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> {
ReactiveCircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> implements DisposableBean {
private final CircuitBreakAPI circuitBreakAPI;
private final ConsumerAPI consumerAPI;
private final ScheduledExecutorService cleanupService = Executors.newSingleThreadScheduledExecutor(
new NamedThreadFactory("sct-reactive-circuitbreaker-cleanup", true));
private Function<String, PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration> defaultConfiguration =
id -> {
@ -41,17 +56,22 @@ public class ReactivePolarisCircuitBreakerFactory extends
return new PolarisCircuitBreakerConfigBuilder()
.namespace(metadata[0])
.service(metadata[1])
.method(metadata[2])
.path(metadata[2])
.protocol(metadata[3])
.method(metadata[4])
.build();
};
private final CircuitBreakAPI circuitBreakAPI;
private final ConsumerAPI consumerAPI;
public ReactivePolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) {
public ReactivePolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
this.circuitBreakAPI = circuitBreakAPI;
this.consumerAPI = consumerAPI;
cleanupService.scheduleWithFixedDelay(
() -> {
getConfigurations().clear();
},
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(),
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(), TimeUnit.MILLISECONDS);
}
@Override
@ -64,7 +84,7 @@ public class ReactivePolarisCircuitBreakerFactory extends
@Override
protected PolarisCircuitBreakerConfigBuilder configBuilder(String id) {
String[] metadata = PolarisCircuitBreakerUtils.resolveCircuitBreakerId(id);
return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2]);
return new PolarisCircuitBreakerConfigBuilder(metadata[0], metadata[1], metadata[2], metadata[3], metadata[4]);
}
@Override
@ -73,4 +93,8 @@ public class ReactivePolarisCircuitBreakerFactory extends
this.defaultConfiguration = defaultConfiguration;
}
@Override
public void destroy() {
ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[] {cleanupService});
}
}

@ -32,15 +32,21 @@ public class PolarisCircuitBreakerConfigBuilder implements ConfigBuilder<Polaris
private String service;
private String protocol;
private String method;
private String path;
public PolarisCircuitBreakerConfigBuilder() {
}
public PolarisCircuitBreakerConfigBuilder(String namespace, String service, String method) {
public PolarisCircuitBreakerConfigBuilder(String namespace, String service, String path, String protocol, String method) {
this.namespace = namespace;
this.service = service;
this.path = path;
this.protocol = protocol;
this.method = method;
}
@ -54,17 +60,29 @@ public class PolarisCircuitBreakerConfigBuilder implements ConfigBuilder<Polaris
return this;
}
public PolarisCircuitBreakerConfigBuilder protocol(String protocol) {
this.protocol = protocol;
return this;
}
public PolarisCircuitBreakerConfigBuilder method(String method) {
this.method = method;
return this;
}
public PolarisCircuitBreakerConfigBuilder path(String path) {
this.path = path;
return this;
}
@Override
public PolarisCircuitBreakerConfiguration build() {
PolarisCircuitBreakerConfiguration conf = new PolarisCircuitBreakerConfiguration();
conf.setNamespace(namespace);
conf.setService(service);
conf.setProtocol(protocol);
conf.setMethod(method);
conf.setPath(path);
return conf;
}
@ -78,8 +96,12 @@ public class PolarisCircuitBreakerConfigBuilder implements ConfigBuilder<Polaris
private String service;
private String protocol;
private String method;
private String path;
public String getNamespace() {
return namespace;
}
@ -96,6 +118,14 @@ public class PolarisCircuitBreakerConfigBuilder implements ConfigBuilder<Polaris
this.service = service;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getMethod() {
return method;
}
@ -104,6 +134,14 @@ public class PolarisCircuitBreakerConfigBuilder implements ConfigBuilder<Polaris
this.method = method;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getSourceNamespace() {
return sourceNamespace;
}

@ -38,6 +38,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
@ -54,6 +55,7 @@ import org.springframework.core.env.Environment;
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisCircuitBreakerEnabled
@EnableConfigurationProperties(PolarisCircuitBreakerProperties.class)
@AutoConfigureAfter(RpcEnhancementAutoConfiguration.class)
public class PolarisCircuitBreakerAutoConfiguration {
@ -89,9 +91,10 @@ public class PolarisCircuitBreakerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public CircuitBreakerFactory polarisCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager) {
public CircuitBreakerFactory polarisCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
PolarisCircuitBreakerFactory factory = new PolarisCircuitBreakerFactory(
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI());
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI(), polarisCircuitBreakerProperties);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}

@ -0,0 +1,57 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Properties of Polaris CircuitBreaker .
*
*/
@ConfigurationProperties("spring.cloud.polaris.circuitbreaker")
public class PolarisCircuitBreakerProperties {
/**
* Whether enable polaris circuit-breaker function.
*/
@Value("${spring.cloud.polaris.circuitbreaker.enabled:#{true}}")
private boolean enabled = true;
/**
* Interval to clean up PolarisCircuitBreakerConfiguration, unit millisecond.
*/
@Value("${spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval:#{300000}}")
private long configurationCleanupInterval = 300000;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public long getConfigurationCleanupInterval() {
return configurationCleanupInterval;
}
public void setConfigurationCleanupInterval(long configurationCleanupInterval) {
this.configurationCleanupInterval = configurationCleanupInterval;
}
}

@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
import org.springframework.context.annotation.Bean;
@ -45,6 +46,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = {"reactor.core.publisher.Mono", "reactor.core.publisher.Flux"})
@ConditionalOnPolarisCircuitBreakerEnabled
@EnableConfigurationProperties(PolarisCircuitBreakerProperties.class)
@AutoConfigureAfter(RpcEnhancementAutoConfiguration.class)
public class ReactivePolarisCircuitBreakerAutoConfiguration {
@ -67,9 +69,10 @@ public class ReactivePolarisCircuitBreakerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class)
public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager) {
public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
ReactivePolarisCircuitBreakerFactory factory = new ReactivePolarisCircuitBreakerFactory(
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI());
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI(), polarisCircuitBreakerProperties);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}

@ -41,7 +41,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Selector;
*
* @author wenxuan70
*/
@Endpoint(id = "polaris-circuit-breaker")
@Endpoint(id = "polariscircuitbreaker")
public class PolarisCircuitBreakerEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(PolarisCircuitBreakerEndpoint.class);

@ -22,12 +22,15 @@ import java.net.URI;
import java.net.URISyntaxException;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.api.utils.StringUtils;
import feign.Request;
import feign.Target;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
@ -45,7 +48,7 @@ public class PolarisCircuitBreakerNameResolver {
String path = "";
// Get path in @FeignClient.
if (StringUtils.hasText(target.url())) {
if (StringUtils.isNotBlank(target.url())) {
URI uri = null;
try {
uri = new URI(target.url());
@ -58,16 +61,24 @@ public class PolarisCircuitBreakerNameResolver {
}
}
// Get path in @RequestMapping.
// Get path and method in @RequestMapping.
RequestMapping requestMapping = findMergedAnnotation(method, RequestMapping.class);
String httpMethod = Request.HttpMethod.GET.name();
if (requestMapping != null) {
path += requestMapping.path().length == 0 ?
requestMapping.value().length == 0 ? "" : requestMapping.value()[0] :
requestMapping.path()[0];
RequestMethod[] requestMethods = requestMapping.method();
if (CollectionUtils.isNotEmpty(requestMethods)) {
httpMethod = requestMethods[0].name();
}
}
return "".equals(path) ?
return StringUtils.isBlank(path) ?
MetadataContext.LOCAL_NAMESPACE + "#" + serviceName :
MetadataContext.LOCAL_NAMESPACE + "#" + serviceName + "#" + path;
MetadataContext.LOCAL_NAMESPACE + "#" + serviceName + "#" + path + "#http#" + httpMethod;
}
}

@ -76,24 +76,6 @@ public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHa
this.decoder = decoder;
}
/**
* If the method param of {@link InvocationHandler#invoke(Object, Method, Object[])}
* is not accessible, i.e in a package-private interface, the fallback call will cause
* of access restrictions. But methods in dispatch are copied methods. So setting
* access to dispatch method doesn't take effect to the method in
* InvocationHandler.invoke. Use map to store a copy of method to invoke the fallback
* to bypass this and reducing the count of reflection calls.
* @return cached methods map for fallback invoking
*/
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
Map<Method, Method> result = new LinkedHashMap<>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// early exit if the invoked method is from java.lang.Object
@ -113,11 +95,14 @@ public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHa
else if ("toString".equals(method.getName())) {
return toString();
}
Supplier<Object> supplier = asSupplier(method, args);
if (circuitBreakerNameResolver != null) {
// disable feign.hystrix
if (circuitBreakerNameResolver == null) {
return this.dispatch.get(method).invoke(args);
}
String circuitName = circuitBreakerNameResolver.resolveCircuitBreakerName(feignClientName, target, method);
CircuitBreaker circuitBreaker = factory.create(circuitName);
Supplier<Object> supplier = asSupplier(method, args);
Function<Throwable, Object> fallbackFunction;
if (this.nullableFallbackFactory != null) {
fallbackFunction = throwable -> {
@ -146,10 +131,6 @@ public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHa
throw e.getCause();
}
}
else {
return supplier.get();
}
}
private void unwrapAndRethrow(Exception exception) {
if (exception instanceof InvocationTargetException || exception instanceof NoFallbackAvailableException) {
@ -189,6 +170,24 @@ public class PolarisFeignCircuitBreakerInvocationHandler implements InvocationHa
};
}
/**
* If the method param of {@link InvocationHandler#invoke(Object, Method, Object[])}
* is not accessible, i.e in a package-private interface, the fallback call will cause
* of access restrictions. But methods in dispatch are copied methods. So setting
* access to dispatch method doesn't take effect to the method in
* InvocationHandler.invoke. Use map to store a copy of method to invoke the fallback
* to bypass this and reducing the count of reflection calls.
* @return cached methods map for fallback invoking
*/
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
Map<Method, Method> result = new LinkedHashMap<>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PolarisFeignCircuitBreakerInvocationHandler) {

@ -28,6 +28,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
import reactor.core.publisher.Flux;
@ -186,10 +187,11 @@ public class PolarisCircuitBreakerFilterFactory extends SpringCloudCircuitBreake
serviceName = route.getUri().getHost();
}
String path = exchange.getRequest().getPath().value();
ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(serviceName + "#" + path);
String method = exchange.getRequest().getMethod() == null ?
"GET" : exchange.getRequest().getMethod().name();
ReactiveCircuitBreaker cb = reactiveCircuitBreakerFactory.create(MetadataContext.LOCAL_NAMESPACE + "#" + serviceName + "#" + path + "#http#" + method);
return cb.run(
chain.filter(exchange)
.doOnSuccess(v -> {
chain.filter(exchange).doOnSuccess(v -> {
// throw CircuitBreakerStatusCodeException by default for all need checking status
// so polaris can report right error status
Set<HttpStatus> statusNeedToCheck = new HashSet<>();

@ -20,6 +20,7 @@ package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.io.IOException;
import java.lang.reflect.Method;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.polaris.circuitbreaker.exception.FallbackWrapperException;
import com.tencent.polaris.api.pojo.CircuitBreakerStatus;
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
@ -65,7 +66,12 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
try {
return circuitBreakerFactory.create(request.getURI().getHost() + "#" + request.getURI().getPath()).run(
String httpMethod = "GET";
if (request.getMethod() != null) {
httpMethod = request.getMethod().name();
}
return circuitBreakerFactory.create(MetadataContext.LOCAL_NAMESPACE + "#" + request.getURI()
.getHost() + "#" + request.getURI().getPath() + "#http#" + httpMethod).run(
() -> {
try {
ClientHttpResponse response = execution.execute(request, body);
@ -84,7 +90,8 @@ public class PolarisCircuitBreakerRestTemplateInterceptor implements ClientHttpR
CircuitBreakerStatus.FallbackInfo fallbackInfo = new CircuitBreakerStatus.FallbackInfo(200, null, polarisCircuitBreaker.fallback());
return new PolarisCircuitBreakerHttpResponse(fallbackInfo);
}
if (!PolarisCircuitBreakerFallback.class.toGenericString().equals(polarisCircuitBreaker.fallbackClass().toGenericString())) {
if (!PolarisCircuitBreakerFallback.class.toGenericString()
.equals(polarisCircuitBreaker.fallbackClass().toGenericString())) {
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerFallback.class, "fallback");
PolarisCircuitBreakerFallback polarisCircuitBreakerFallback = applicationContext.getBean(polarisCircuitBreaker.fallbackClass());
return (PolarisCircuitBreakerHttpResponse) ReflectionUtils.invokeMethod(method, polarisCircuitBreakerFallback);

@ -45,29 +45,36 @@ public final class PolarisCircuitBreakerUtils {
}
/**
* Format:
* 0. namespace#service#path#protocol#method
* 1. namespace#service#method
* 2. service#method
* 3. service
* namespace set as default spring.cloud.polaris.namespace if absent.
*
* @param id CircuitBreakerId
* Format: namespace#service#method or service#method or service ,
* namespace set as default spring.cloud.polaris.namespace if absent
* @return String[]{namespace, service, method}
*/
public static String[] resolveCircuitBreakerId(String id) {
Assert.hasText(id, "A CircuitBreaker must have an id. Id could be : namespace#service#method or service#method or service");
String[] polarisCircuitBreakerMetaData = id.split("#");
if (polarisCircuitBreakerMetaData.length == 2) {
return new String[]{MetadataContext.LOCAL_NAMESPACE, polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1]};
return new String[] {MetadataContext.LOCAL_NAMESPACE, polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1], "http", ""};
}
if (polarisCircuitBreakerMetaData.length == 3) {
return new String[]{polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1], polarisCircuitBreakerMetaData[2]};
return new String[] {polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1], polarisCircuitBreakerMetaData[2], "http", ""};
}
return new String[]{MetadataContext.LOCAL_NAMESPACE, id, ""};
if (polarisCircuitBreakerMetaData.length == 5) {
return new String[] {polarisCircuitBreakerMetaData[0], polarisCircuitBreakerMetaData[1], polarisCircuitBreakerMetaData[2], polarisCircuitBreakerMetaData[3], polarisCircuitBreakerMetaData[4]};
}
return new String[] {MetadataContext.LOCAL_NAMESPACE, id, "", "http", ""};
}
public static void reportStatus(ConsumerAPI consumerAPI,
PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration conf, CallAbortedException e) {
try {
ServiceCallResult result = new ServiceCallResult();
result.setMethod(conf.getMethod());
result.setMethod(conf.getPath());
result.setNamespace(conf.getNamespace());
result.setService(conf.getService());
result.setRuleName(e.getRuleName());

@ -97,11 +97,12 @@ public class PolarisCircuitBreakerZuulFilter extends ZuulFilter {
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
String serviceId = ZuulFilterUtils.getServiceId(context);
String path = ZuulFilterUtils.getPath(context);
String circuitName = "".equals(path) ?
String circuitName = com.tencent.polaris.api.utils.StringUtils.isBlank(path) ?
MetadataContext.LOCAL_NAMESPACE + "#" + serviceId :
MetadataContext.LOCAL_NAMESPACE + "#" + serviceId + "#" + path;
MetadataContext.LOCAL_NAMESPACE + "#" + serviceId + "#" + path + "#http#" + context.getRequest().getMethod();
CircuitBreaker circuitBreaker = circuitBreakerFactory.create(circuitName);
if (circuitBreaker instanceof PolarisCircuitBreaker) {
PolarisCircuitBreaker polarisCircuitBreaker = (PolarisCircuitBreaker) circuitBreaker;

@ -36,7 +36,8 @@ public class PolarisFeignCircuitBreakerTargeter implements Targeter {
private final PolarisCircuitBreakerNameResolver circuitBreakerNameResolver;
public PolarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, PolarisCircuitBreakerNameResolver circuitBreakerNameResolver) {
public PolarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory,
PolarisCircuitBreakerNameResolver circuitBreakerNameResolver) {
this.circuitBreakerFactory = circuitBreakerFactory;
this.circuitBreakerNameResolver = circuitBreakerNameResolver;
}

@ -3,7 +3,14 @@
{
"name": "spring.cloud.polaris.circuitbreaker.enabled",
"type": "java.lang.Boolean",
"defaultValue": "true"
"defaultValue": "true",
"description": "If polaris circuitbreaker enabled."
},
{
"name": "spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval",
"type": "java.lang.Long",
"defaultValue": "300000",
"description": "Interval to clean up PolarisCircuitBreakerConfiguration, unit millisecond."
}
],
"hints": []

@ -30,12 +30,14 @@ import java.util.stream.Collectors;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.polaris.api.config.Configuration;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
@ -70,6 +72,8 @@ public class PolarisCircuitBreakerMockServerTest {
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
private static NamingServer namingServer;
private static SDKContext context;
@BeforeAll
public static void beforeAll() throws IOException {
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
@ -78,11 +82,10 @@ public class PolarisCircuitBreakerMockServerTest {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service"))
.thenReturn(SERVICE_CIRCUIT_BREAKER);
PolarisSDKContextManager.innerDestroy();
namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_CIRCUIT_BREAKER);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerMockServerTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
@ -93,6 +96,9 @@ public class PolarisCircuitBreakerMockServerTest {
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
Configuration configuration = TestUtils.configWithEnvAddress();
context = SDKContext.initContextByConfig(configuration);
}
@AfterAll
@ -103,15 +109,17 @@ public class PolarisCircuitBreakerMockServerTest {
if (null != mockedApplicationContextAwareUtils) {
mockedApplicationContextAwareUtils.close();
}
if (null != context) {
context.close();
}
}
@Test
public void testCircuitBreaker() {
Configuration configuration = TestUtils.configWithEnvAddress();
CircuitBreakAPI circuitBreakAPI = CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration);
PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = new PolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI);
CircuitBreakAPI circuitBreakAPI = CircuitBreakAPIFactory.createCircuitBreakAPIByContext(context);
ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(context);
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties = new PolarisCircuitBreakerProperties();
PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = new PolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI, polarisCircuitBreakerProperties);
CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);
// trigger fallback for 5 times
@ -132,7 +140,7 @@ public class PolarisCircuitBreakerMockServerTest {
assertThat(resList).isEqualTo(Arrays.asList("invoke success", "fallback", "fallback", "fallback", "fallback"));
// always fallback
ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI);
ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI, polarisCircuitBreakerProperties);
ReactiveCircuitBreaker rcb = reactivePolarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);
assertThat(Mono.just("foobar").transform(it -> rcb.run(it, t -> Mono.just("fallback")))

@ -18,13 +18,19 @@
package com.tencent.cloud.polaris.circuitbreaker;
import java.lang.reflect.Method;
import java.util.Map;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ReflectionUtils;
import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration;
import com.tencent.polaris.client.util.Utils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -56,7 +62,8 @@ public class PolarisCircuitBreakerTest {
LoadBalancerAutoConfiguration.class,
PolarisCircuitBreakerFeignClientAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval=5000");
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
@ -92,6 +99,18 @@ public class PolarisCircuitBreakerTest {
throw new RuntimeException("boom");
}, t -> "fallback")).isEqualTo("fallback");
Method getConfigurationsMethod = ReflectionUtils.findMethod(PolarisCircuitBreakerFactory.class,
"getConfigurations");
Assertions.assertNotNull(getConfigurationsMethod);
ReflectionUtils.makeAccessible(getConfigurationsMethod);
Map<?, ?> values = (Map<?, ?>) ReflectionUtils.invokeMethod(getConfigurationsMethod, polarisCircuitBreakerFactory);
Assertions.assertNotNull(values);
Assertions.assertEquals(1, values.size());
Utils.sleepUninterrupted(10 * 1000);
Assertions.assertEquals(0, values.size());
});
}

@ -17,15 +17,20 @@
package com.tencent.cloud.polaris.circuitbreaker;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ReflectionUtils;
import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
import com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration;
import com.tencent.polaris.client.util.Utils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -58,7 +63,8 @@ public class ReactivePolarisCircuitBreakerTest {
RpcEnhancementAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
ReactivePolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval=5000");
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
@ -97,6 +103,18 @@ public class ReactivePolarisCircuitBreakerTest {
assertThat(Flux.error(new RuntimeException("boom")).transform(it -> cb.run(it, t -> Flux.just("fallback")))
.collectList().block()).isEqualTo(Collections.singletonList("fallback"));
Method getConfigurationsMethod = ReflectionUtils.findMethod(PolarisCircuitBreakerFactory.class,
"getConfigurations");
Assertions.assertNotNull(getConfigurationsMethod);
ReflectionUtils.makeAccessible(getConfigurationsMethod);
Map<?, ?> values = (Map<?, ?>) ReflectionUtils.invokeMethod(getConfigurationsMethod, polarisCircuitBreakerFactory);
Assertions.assertNotNull(values);
Assertions.assertTrue(values.size() >= 0);
Utils.sleepUninterrupted(10 * 1000);
// clear by cleanupService in ReactivePolarisCircuitBreakerFactory
Assertions.assertEquals(0, values.size());
});
}

@ -17,8 +17,10 @@
package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -42,6 +44,11 @@ public class PolarisCircuitBreakerBootstrapConfigurationTest {
.withPropertyValues("spring.cloud.polaris.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@BeforeAll
static void beforeAll() {
PolarisSDKContextManager.innerDestroy();
}
@Test
public void testDefaultInitialization() {
this.contextRunner.run(context -> {

@ -19,47 +19,32 @@ package com.tencent.cloud.polaris.circuitbreaker.feign;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter;
import com.tencent.cloud.polaris.circuitbreaker.reporter.SuccessCircuitBreakerReporter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
import com.tencent.polaris.test.common.TestUtils;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.PolarisFeignCircuitBreakerTargeter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@ -68,7 +53,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@ -82,6 +66,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
properties = {
"feign.hystrix.enabled=true",
"spring.cloud.gateway.enabled=false",
"spring.cloud.polaris.address=grpc://127.0.0.1:10081",
"feign.circuitbreaker.enabled=true",
"spring.cloud.polaris.namespace=" + NAMESPACE_TEST,
"spring.cloud.polaris.service=test"
@ -90,6 +75,8 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee";
private static NamingServer namingServer;
@Autowired
private EchoService echoService;
@ -102,6 +89,31 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
@Autowired
private BazService bazService;
@BeforeAll
static void beforeAll() throws Exception {
PolarisSDKContextManager.innerDestroy();
namingServer = NamingServer.startNamingServer(10081);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerFeignIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
}
@AfterAll
static void afterAll() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void contextLoads() {
assertThat(echoService).isNotNull();
@ -170,9 +182,6 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
@EnableFeignClients
public static class TestConfig {
@Autowired(required = false)
private List<Customizer<PolarisCircuitBreakerFactory>> customizers = new ArrayList<>();
@Bean
public EchoServiceFallback echoServiceFallback() {
return new EchoServiceFallback();
@ -182,74 +191,6 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
public CustomFallbackFactory customFallbackFactory() {
return new CustomFallbackFactory();
}
@Bean
public PreDestroy preDestroy(NamingServer namingServer) {
return new PreDestroy(namingServer);
}
@Bean
public NamingServer namingServer() throws IOException {
NamingServer namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerFeignIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
return namingServer;
}
@Bean
public CircuitBreakAPI circuitBreakAPI(NamingServer namingServer) {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
}
@Bean
public ConsumerAPI consumerAPI(NamingServer namingServer) {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return DiscoveryAPIFactory.createConsumerAPIByConfig(configuration);
}
@Bean
public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties,
CircuitBreakAPI circuitBreakAPI) {
return new SuccessCircuitBreakerReporter(properties, circuitBreakAPI);
}
@Bean
public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties,
CircuitBreakAPI circuitBreakAPI) {
return new ExceptionCircuitBreakerReporter(properties, circuitBreakAPI);
}
@Bean
public CircuitBreakerFactory polarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) {
PolarisCircuitBreakerFactory factory = new PolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}
@Bean
public PolarisCircuitBreakerNameResolver polarisCircuitBreakerNameResolver() {
return new PolarisCircuitBreakerNameResolver();
}
@Bean
@Primary
@ConditionalOnBean(CircuitBreakerFactory.class)
@ConditionalOnProperty(value = "feign.hystrix.enabled", havingValue = "true")
public PolarisFeignCircuitBreakerTargeter polarisFeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, PolarisCircuitBreakerNameResolver circuitBreakerNameResolver) {
return new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver);
}
}
public static class EchoServiceFallback implements EchoService {
@ -284,19 +225,4 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
}
}
public static class PreDestroy implements DisposableBean {
private final NamingServer namingServer;
public PreDestroy(NamingServer namingServer) {
this.namingServer = namingServer;
}
@Override
public void destroy() throws Exception {
namingServer.terminate();
}
}
}

@ -93,7 +93,7 @@ public class PolarisCircuitBreakerNameResolverTest {
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerNameResolverTest.class, "mockRequestMapping2");
PolarisCircuitBreakerNameResolver resolver = new PolarisCircuitBreakerNameResolver();
String polarisCircuitBreakerName = resolver.resolveCircuitBreakerName("test", target, method);
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc#/");
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc#/#http#GET");
}
@Test
@ -103,7 +103,7 @@ public class PolarisCircuitBreakerNameResolverTest {
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerNameResolverTest.class, "mockRequestMapping3");
PolarisCircuitBreakerNameResolver resolver = new PolarisCircuitBreakerNameResolver();
String polarisCircuitBreakerName = resolver.resolveCircuitBreakerName("test", target, method);
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc#/");
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc#/#http#GET");
}

@ -19,43 +19,30 @@ package com.tencent.cloud.polaris.circuitbreaker.gateway;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory;
import com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter;
import com.tencent.cloud.polaris.circuitbreaker.reporter.SuccessCircuitBreakerReporter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
import com.tencent.polaris.test.common.TestUtils;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.gateway.filter.factory.SpringCloudCircuitBreakerFilterFactory;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
@ -69,8 +56,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.SERVICE_CIRCUIT_BREAKER;
import static com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV;
import static org.assertj.core.api.Assertions.assertThat;
@ -83,7 +68,7 @@ import static org.assertj.core.api.Assertions.assertThat;
properties = {
"spring.cloud.gateway.enabled=true",
"spring.cloud.polaris.namespace=" + NAMESPACE_TEST,
"spring.cloud.polaris.service=" + SERVICE_CIRCUIT_BREAKER,
"spring.cloud.polaris.service=test",
"spring.main.web-application-type=reactive"
},
classes = PolarisCircuitBreakerGatewayIntegrationTest.TestApplication.class
@ -94,12 +79,39 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee";
private static NamingServer namingServer;
@Autowired
private WebTestClient webClient;
@Autowired
private ApplicationContext applicationContext;
@BeforeAll
static void beforeAll() throws Exception {
PolarisSDKContextManager.innerDestroy();
namingServer = NamingServer.startNamingServer(10081);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerGatewayIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
}
@AfterAll
static void afterAll() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void fallback() throws Exception {
SpringCloudCircuitBreakerFilterFactory.Config config = new SpringCloudCircuitBreakerFilterFactory.Config();
@ -161,67 +173,6 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
@EnableAutoConfiguration
public static class TestApplication {
@Autowired(required = false)
private List<Customizer<PolarisCircuitBreakerFactory>> customizers = new ArrayList<>();
@Bean
public PreDestroy preDestroy(NamingServer namingServer) {
return new PreDestroy(namingServer);
}
@Bean
public NamingServer namingServer() throws IOException {
NamingServer namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerGatewayIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
return namingServer;
}
@Bean
public CircuitBreakAPI circuitBreakAPI(NamingServer namingServer) throws IOException {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
}
@Bean
public ConsumerAPI consumerAPI(NamingServer namingServer) {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return DiscoveryAPIFactory.createConsumerAPIByConfig(configuration);
}
@Bean
@ConditionalOnMissingBean(SuccessCircuitBreakerReporter.class)
public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties,
CircuitBreakAPI circuitBreakAPI) {
return new SuccessCircuitBreakerReporter(properties, circuitBreakAPI);
}
@Bean
@ConditionalOnMissingBean(ExceptionCircuitBreakerReporter.class)
public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties,
CircuitBreakAPI circuitBreakAPI) {
return new ExceptionCircuitBreakerReporter(properties, circuitBreakAPI);
}
@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public CircuitBreakerFactory polarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) {
PolarisCircuitBreakerFactory factory = new PolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
Set<String> codeSets = new HashSet<>();
@ -264,19 +215,4 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
}
}
public static class PreDestroy implements DisposableBean {
private final NamingServer namingServer;
public PreDestroy(NamingServer namingServer) {
this.namingServer = namingServer;
}
@Override
public void destroy() throws Exception {
namingServer.terminate();
}
}
}

@ -18,42 +18,31 @@
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter;
import com.tencent.cloud.polaris.circuitbreaker.reporter.SuccessCircuitBreakerReporter;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
import com.tencent.polaris.test.common.TestUtils;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ApplicationContext;
@ -72,7 +61,6 @@ import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@ -88,6 +76,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
classes = PolarisCircuitBreakerRestTemplateIntegrationTest.TestConfig.class,
properties = {
"spring.cloud.gateway.enabled=false",
"spring.cloud.polaris.address=grpc://127.0.0.1:10081",
"feign.circuitbreaker.enabled=true",
"spring.cloud.polaris.namespace=" + NAMESPACE_TEST,
"spring.cloud.polaris.service=test"
@ -96,6 +85,8 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee";
private static NamingServer namingServer;
@Autowired
@Qualifier("defaultRestTemplate")
private RestTemplate defaultRestTemplate;
@ -123,6 +114,29 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@Autowired
private ApplicationContext applicationContext;
@BeforeAll
static void beforeAll() throws Exception {
PolarisSDKContextManager.innerDestroy();
namingServer = NamingServer.startNamingServer(10081);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerRestTemplateIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
}
@AfterAll
static void afterAll() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void testRestTemplate() throws URISyntaxException {
@ -149,7 +163,8 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
Utils.sleepUninterrupted(2000);
assertThat(restTemplateFallbackFromCode2.getForObject("/example/service/b/info", String.class)).isEqualTo("\"this is a fallback class\"");
Utils.sleepUninterrupted(2000);
assertThat(restTemplateFallbackFromCode3.getForEntity("/example/service/b/info", String.class).getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(restTemplateFallbackFromCode3.getForEntity("/example/service/b/info", String.class)
.getStatusCode()).isEqualTo(HttpStatus.OK);
Utils.sleepUninterrupted(2000);
assertThat(restTemplateFallbackFromCode4.getForObject("/example/service/b/info", String.class)).isEqualTo("fallback");
Utils.sleepUninterrupted(2000);
@ -166,13 +181,6 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
@EnableFeignClients
public static class TestConfig {
@Autowired(required = false)
private List<Customizer<PolarisCircuitBreakerFactory>> customizers = new ArrayList<>();
{
PolarisSDKContextManager.innerDestroy();
}
@Bean
@com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallback = "fallback")
public RestTemplate defaultRestTemplate() {
@ -244,56 +252,6 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
return new CustomPolarisCircuitBreakerFallback3();
}
@Bean
public NamingServer namingServer() throws IOException {
NamingServer namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerRestTemplateIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
return namingServer;
}
@Bean
public PreDestroy preDestroy(NamingServer namingServer) {
return new PreDestroy(namingServer);
}
@Bean
public CircuitBreakAPI circuitBreakAPI(NamingServer namingServer) {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
}
@Bean
public SuccessCircuitBreakerReporter successCircuitBreakerReporter(RpcEnhancementReporterProperties properties,
CircuitBreakAPI circuitBreakAPI) {
return new SuccessCircuitBreakerReporter(properties, circuitBreakAPI);
}
@Bean
public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhancementReporterProperties properties,
CircuitBreakAPI circuitBreakAPI) {
return new ExceptionCircuitBreakerReporter(properties, circuitBreakAPI);
}
@Bean
public CircuitBreakerFactory polarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI,
PolarisSDKContextManager polarisSDKContextManager) {
PolarisCircuitBreakerFactory factory = new PolarisCircuitBreakerFactory(
circuitBreakAPI, polarisSDKContextManager.getConsumerAPI());
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}
@RestController
@RequestMapping("/example/service/b")
public class ServiceBController {
@ -342,19 +300,4 @@ public class PolarisCircuitBreakerRestTemplateIntegrationTest {
);
}
}
public static class PreDestroy implements DisposableBean {
private final NamingServer namingServer;
public PreDestroy(NamingServer namingServer) {
this.namingServer = namingServer;
}
@Override
public void destroy() throws Exception {
namingServer.terminate();
}
}
}

@ -71,9 +71,11 @@ public class PolarisCircuitBreakerUtilsTests {
@Test
public void testResolveCircuitBreakerId() {
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_svc")).isEqualTo(new String[]{NAMESPACE_TEST, "test_svc", ""});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_svc#test_path")).isEqualTo(new String[]{NAMESPACE_TEST, "test_svc", "test_path"});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_ns#test_svc#test_path")).isEqualTo(new String[]{"test_ns", "test_svc", "test_path"});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_svc")).isEqualTo(new String[] {NAMESPACE_TEST, "test_svc", "", "http", ""});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_svc#test_path")).isEqualTo(new String[] {NAMESPACE_TEST, "test_svc", "test_path", "http", ""});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_ns#test_svc#test_path")).isEqualTo(new String[] {"test_ns", "test_svc", "test_path", "http", ""});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_ns#test_svc#test_path")).isEqualTo(new String[] {"test_ns", "test_svc", "test_path", "http", ""});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_ns#test_svc#test_path#tcp#POST")).isEqualTo(new String[] {"test_ns", "test_svc", "test_path", "tcp", "POST"});
}
}

@ -1,22 +1,15 @@
spring:
main:
web-application-type: reactive
application:
name: GatewayScgService
cloud:
tencent:
plugin:
scg:
staining:
enabled: true
rule-staining:
enabled: true
router:
feature-env:
enabled: true
polaris:
address: grpc://127.0.0.1:10081
namespace: default
namespace: Test
enabled: true
gateway:
enabled: true
routes:
- id: cb-test
uri: http://localhost:${server.port}/hello/1

@ -34,6 +34,10 @@
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-namespace</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>

@ -13,7 +13,6 @@
* 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;
@ -26,27 +25,27 @@ import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.util.AddressUtils;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.config.PolarisCryptoConfigProperties;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.context.PolarisConfigurationConfigModifier;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.api.utils.CollectionUtils;
import com.tencent.polaris.factory.config.ConfigurationImpl;
import com.tencent.polaris.factory.config.configuration.ConfigFilterConfigImpl;
import com.tencent.polaris.factory.config.configuration.ConnectorConfigImpl;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import static com.tencent.polaris.api.config.plugin.DefaultPlugins.LOCAL_FILE_CONNECTOR_TYPE;
/**
* Read configuration from spring cloud's configuration file and override polaris.yaml.
*
* @author lepdou 2022-03-10
*/
public class ConfigurationModifier implements PolarisConfigModifier {
public class ConfigurationModifier implements PolarisConfigurationConfigModifier {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationModifier.class);
private static final String DATA_SOURCE_POLARIS = "polaris";
private static final String DATA_SOURCE_LOCAL = "local";
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisCryptoConfigProperties polarisCryptoConfigProperties;
@ -63,16 +62,15 @@ public class ConfigurationModifier implements PolarisConfigModifier {
@Override
public void modify(ConfigurationImpl configuration) {
if (StringUtils.equalsIgnoreCase(polarisConfigProperties.getDataSource(), DATA_SOURCE_POLARIS)) {
initByPolarisDataSource(configuration);
}
else if (StringUtils.equalsIgnoreCase(polarisConfigProperties.getDataSource(), DATA_SOURCE_LOCAL)) {
initByLocalDataSource(configuration);
}
else {
throw new RuntimeException("Unsupported config data source");
configuration.getGlobal().getAPI().setReportEnable(false);
configuration.getGlobal().getStatReporter().setEnable(false);
if (!polarisContextProperties.getEnabled() || !polarisConfigProperties.isEnabled()) {
return;
}
initDataSource(configuration);
ConfigFilterConfigImpl configFilterConfig = configuration.getConfigFile().getConfigFilterConfig();
configFilterConfig.setEnable(polarisCryptoConfigProperties.isEnabled());
if (polarisCryptoConfigProperties.isEnabled()) {
@ -81,19 +79,16 @@ public class ConfigurationModifier implements PolarisConfigModifier {
}
}
private void initByLocalDataSource(ConfigurationImpl configuration) {
configuration.getConfigFile().getServerConnector().setConnectorType("localFile");
private void initDataSource(ConfigurationImpl configuration) {
// set connector type
configuration.getConfigFile().getServerConnector().setConnectorType(polarisConfigProperties.getDataSource());
if (StringUtils.equalsIgnoreCase(polarisConfigProperties.getDataSource(), LOCAL_FILE_CONNECTOR_TYPE)) {
String localFileRootPath = polarisConfigProperties.getLocalFileRootPath();
configuration.getConfigFile().getServerConnector().setPersistDir(localFileRootPath);
LOGGER.info("[SCT] Run spring cloud tencent config with local data source. localFileRootPath = {}", localFileRootPath);
return;
}
private void initByPolarisDataSource(ConfigurationImpl configuration) {
// set connector type
configuration.getConfigFile().getServerConnector().setConnectorType("polaris");
// set config server address
List<String> configAddresses;
String configAddressesStr = polarisConfigProperties.getAddress();
@ -114,6 +109,11 @@ public class ConfigurationModifier implements PolarisConfigModifier {
configuration.getConfigFile().getServerConnector().setAddresses(configAddresses);
if (StringUtils.isNotEmpty(polarisConfigProperties.getToken())) {
ConnectorConfigImpl connectorConfig = configuration.getConfigFile().getServerConnector();
connectorConfig.setToken(polarisConfigProperties.getToken());
}
LOGGER.info("[SCT] Run spring cloud tencent config in polaris data source.");
}

@ -21,7 +21,6 @@ package com.tencent.cloud.polaris.config;
import com.tencent.cloud.polaris.config.adapter.AffectedConfigurationPropertiesRebinder;
import com.tencent.cloud.polaris.config.adapter.PolarisConfigPropertyRefresher;
import com.tencent.cloud.polaris.config.adapter.PolarisConfigRefreshScopeAnnotationDetector;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.adapter.PolarisRefreshAffectedContextRefresher;
import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher;
import com.tencent.cloud.polaris.config.annotation.PolarisConfigAnnotationProcessor;
@ -68,7 +67,6 @@ public class PolarisConfigAutoConfiguration {
return new PolarisConfigLoggerApplicationListener();
}
@Bean
@Primary
@ConditionalOnReflectRefreshType
@ -79,9 +77,9 @@ public class PolarisConfigAutoConfiguration {
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public PolarisConfigPropertyRefresher polarisRefreshContextPropertySourceAutoRefresher(PolarisConfigProperties polarisConfigProperties,
PolarisPropertySourceManager polarisPropertySourceManager, ContextRefresher contextRefresher) {
return new PolarisRefreshEntireContextRefresher(polarisConfigProperties, polarisPropertySourceManager, contextRefresher);
public PolarisConfigPropertyRefresher polarisRefreshContextPropertySourceAutoRefresher(
PolarisConfigProperties polarisConfigProperties, ContextRefresher contextRefresher) {
return new PolarisRefreshEntireContextRefresher(polarisConfigProperties, contextRefresher);
}
@Configuration(proxyBeanMethods = false)
@ -105,10 +103,10 @@ public class PolarisConfigAutoConfiguration {
}
@Bean
public PolarisConfigPropertyRefresher polarisReflectPropertySourceAutoRefresher(PolarisConfigProperties polarisConfigProperties,
PolarisPropertySourceManager polarisPropertySourceManager, SpringValueRegistry springValueRegistry,
public PolarisConfigPropertyRefresher polarisReflectPropertySourceAutoRefresher(
PolarisConfigProperties polarisConfigProperties, SpringValueRegistry springValueRegistry,
PlaceholderHelper placeholderHelper) {
return new PolarisRefreshAffectedContextRefresher(polarisConfigProperties, polarisPropertySourceManager,
return new PolarisRefreshAffectedContextRefresher(polarisConfigProperties,
springValueRegistry, placeholderHelper);
}

@ -20,7 +20,6 @@ package com.tencent.cloud.polaris.config;
import com.tencent.cloud.polaris.config.adapter.AffectedConfigurationPropertiesRebinder;
import com.tencent.cloud.polaris.config.adapter.PolarisConfigFileLocator;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.condition.ConditionalOnReflectRefreshType;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.config.PolarisCryptoConfigProperties;
@ -37,6 +36,7 @@ import org.springframework.cloud.context.properties.ConfigurationPropertiesRebin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
/**
@ -59,16 +59,10 @@ public class PolarisConfigBootstrapAutoConfiguration {
return new PolarisCryptoConfigProperties();
}
@Bean
@ConditionalOnMissingBean
public PolarisPropertySourceManager polarisPropertySourceManager() {
return new PolarisPropertySourceManager();
}
@Bean
@ConditionalOnConnectRemoteServerEnabled
public ConfigFileService configFileService(PolarisSDKContextManager polarisSDKContextManager) {
return ConfigFileServiceFactory.createConfigFileService(polarisSDKContextManager.getSDKContext());
return ConfigFileServiceFactory.createConfigFileService(polarisSDKContextManager.getConfigSDKContext());
}
@Bean
@ -77,11 +71,9 @@ public class PolarisConfigBootstrapAutoConfiguration {
PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties,
ConfigFileService configFileService,
PolarisPropertySourceManager polarisPropertySourceManager,
Environment environment) {
return new PolarisConfigFileLocator(polarisConfigProperties,
polarisContextProperties, configFileService,
polarisPropertySourceManager, environment);
polarisContextProperties, configFileService, environment);
}
@Bean
@ -93,6 +85,7 @@ public class PolarisConfigBootstrapAutoConfiguration {
}
@Bean
@Primary
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
@ConditionalOnReflectRefreshType
public ConfigurationPropertiesRebinder affectedConfigurationPropertiesRebinder(

@ -43,13 +43,16 @@ import org.springframework.util.StringUtils;
/**
* Optimize {@link ConfigurationPropertiesRebinder}, only rebuild affected beans.
* @author weihubeats 2022-7-10
*
* @author weihubeats
*/
public class AffectedConfigurationPropertiesRebinder extends ConfigurationPropertiesRebinder {
private static final Logger LOGGER = LoggerFactory.getLogger(AffectedConfigurationPropertiesRebinder.class);
private ApplicationContext applicationContext;
private Map<String, ConfigurationPropertiesBean> propertiesBeans = new HashMap<>();
private final Map<String, Map<String, Object>> propertiesBeanDefaultValues = new ConcurrentHashMap<>();
public AffectedConfigurationPropertiesRebinder(ConfigurationPropertiesBeans beans) {
@ -125,8 +128,7 @@ public class AffectedConfigurationPropertiesRebinder extends ConfigurationProper
for (ConfigurationPropertiesBean propertiesBean : propertiesBeans.values()) {
Map<String, Object> defaultValues = new HashMap<>();
try {
Object instance = propertiesBean.getInstance().getClass().getDeclaredConstructor((Class<?>[]) null)
.newInstance();
Object instance = propertiesBean.getInstance().getClass().getDeclaredConstructor((Class<?>[]) null).newInstance();
ReflectionUtils.doWithFields(instance.getClass(), field -> {
try {
field.setAccessible(true);

@ -29,10 +29,13 @@ import org.springframework.core.env.Environment;
* @author juanyinyang
*/
public interface PolarisConfigCustomExtensionLayer {
boolean isEnabled();
void initRegisterConfig(PolarisConfigPropertyAutoRefresher polarisConfigPropertyAutoRefresher);
void initConfigFiles(Environment environment, CompositePropertySource compositePropertySource, PolarisPropertySourceManager polarisPropertySourceManager, ConfigFileService configFileService);
void initConfigFiles(Environment environment, CompositePropertySource compositePropertySource, ConfigFileService configFileService);
void executeAfterLocateConfigReturning(CompositePropertySource compositePropertySource);
boolean executeRegisterPublishChangeListener(PolarisPropertySource polarisPropertySource);
}

@ -13,7 +13,6 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.adapter;
@ -65,22 +64,21 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
private final ConfigFileService configFileService;
private final PolarisPropertySourceManager polarisPropertySourceManager;
private final Environment environment;
// this class provides customized logic for some customers to configure special business group files
private final PolarisConfigCustomExtensionLayer polarisConfigCustomExtensionLayer = PolarisServiceLoaderUtil.getPolarisConfigCustomExtensionLayer();
public PolarisConfigFileLocator(PolarisConfigProperties polarisConfigProperties, PolarisContextProperties polarisContextProperties, ConfigFileService configFileService, PolarisPropertySourceManager polarisPropertySourceManager, Environment environment) {
public PolarisConfigFileLocator(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties, ConfigFileService configFileService, Environment environment) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisContextProperties = polarisContextProperties;
this.configFileService = configFileService;
this.polarisPropertySourceManager = polarisPropertySourceManager;
this.environment = environment;
}
@Override
public PropertySource<?> locate(Environment environment) {
if (polarisConfigProperties.isEnabled()) {
CompositePropertySource compositePropertySource = new CompositePropertySource(POLARIS_CONFIG_PROPERTY_SOURCE_NAME);
try {
// load custom config extension files
@ -99,13 +97,15 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
afterLocatePolarisConfigExtension(compositePropertySource);
}
}
return null;
}
private void initCustomPolarisConfigExtensionFiles(CompositePropertySource compositePropertySource) {
if (polarisConfigCustomExtensionLayer == null) {
LOGGER.debug("[SCT Config] PolarisConfigCustomExtensionLayer is not init, ignore the following execution steps");
return;
}
polarisConfigCustomExtensionLayer.initConfigFiles(environment, compositePropertySource, polarisPropertySourceManager, configFileService);
polarisConfigCustomExtensionLayer.initConfigFiles(environment, compositePropertySource, configFileService);
}
private void afterLocatePolarisConfigExtension(CompositePropertySource compositePropertySource) {
@ -117,6 +117,9 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
}
private void initInternalConfigFiles(CompositePropertySource compositePropertySource) {
if (!polarisConfigProperties.isInternalEnabled()) {
return;
}
List<ConfigFileMetadata> internalConfigFiles = getInternalConfigFiles();
for (ConfigFileMetadata configFile : internalConfigFiles) {
@ -124,7 +127,7 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource);
PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info("[SCT Config] Load and inject polaris config file. file = {}", configFile);
}
@ -191,8 +194,12 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
String namespace = polarisContextProperties.getNamespace();
for (ConfigFileGroup configFileGroup : configFileGroups) {
String group = configFileGroup.getName();
String groupNamespace = configFileGroup.getNamespace();
if (!StringUtils.hasText(groupNamespace)) {
groupNamespace = namespace;
}
String group = configFileGroup.getName();
if (!StringUtils.hasText(group)) {
throw new IllegalArgumentException("polaris config group name cannot be empty.");
}
@ -203,26 +210,26 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
}
for (String fileName : files) {
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(namespace, group, fileName);
PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(groupNamespace, group, fileName);
compositePropertySource.addPropertySource(polarisPropertySource);
polarisPropertySourceManager.addPropertySource(polarisPropertySource);
PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
LOGGER.info("[SCT Config] Load and inject polaris config file success. namespace = {}, group = {}, fileName = {}", namespace, group, fileName);
LOGGER.info("[SCT Config] Load and inject polaris config file success. namespace = {}, group = {}, fileName = {}", groupNamespace, group, fileName);
}
}
}
private PolarisPropertySource loadPolarisPropertySource(String namespace, String group, String fileName) {
ConfigKVFile configKVFile;
// unknown extension is resolved as properties file
if (ConfigFileFormat.isPropertyFile(fileName) || ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
}
else if (ConfigFileFormat.isYamlFile(fileName)) {
// unknown extension is resolved as yaml file
if (ConfigFileFormat.isYamlFile(fileName) || ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName);
}
else if (ConfigFileFormat.isPropertyFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
}
else {
LOGGER.warn("[SCT Config] Unsupported config file. namespace = {}, group = {}, fileName = {}", namespace, group, fileName);

@ -18,14 +18,17 @@
package com.tencent.cloud.polaris.config.adapter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.logger.PolarisConfigLoggerContext;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import com.tencent.polaris.configuration.client.internal.CompositeConfigFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,7 +41,7 @@ import org.springframework.util.CollectionUtils;
* 1. Listen to the Polaris server configuration publishing event 2. Write the changed
* configuration content to propertySource 3. Refresh the context through contextRefresher
*
* @author lepdou 2022-03-28
* @author lepdou
*/
public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationListener<ApplicationReadyEvent>, PolarisConfigPropertyRefresher {
@ -46,16 +49,13 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisPropertySourceManager polarisPropertySourceManager;
private final AtomicBoolean registered = new AtomicBoolean(false);
// this class provides customized logic for some customers to configure special business group files
private final PolarisConfigCustomExtensionLayer polarisConfigCustomExtensionLayer = PolarisServiceLoaderUtil.getPolarisConfigCustomExtensionLayer();
public PolarisConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) {
public PolarisConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisPropertySourceManager = polarisPropertySourceManager;
}
@Override
@ -68,7 +68,7 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
return;
}
List<PolarisPropertySource> polarisPropertySources = polarisPropertySourceManager.getAllPropertySources();
List<PolarisPropertySource> polarisPropertySources = PolarisPropertySourceManager.getAllPropertySources();
if (CollectionUtils.isEmpty(polarisPropertySources)) {
return;
}
@ -82,10 +82,20 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
// register polaris config publish event
for (PolarisPropertySource polarisPropertySource : polarisPropertySources) {
if (polarisPropertySource.getConfigKVFile() instanceof CompositeConfigFile) {
CompositeConfigFile configKVFile = (CompositeConfigFile) polarisPropertySource.getConfigKVFile();
for (ConfigKVFile cf : configKVFile.getConfigKVFiles()) {
PolarisPropertySource p = new PolarisPropertySource(cf.getNamespace(), cf.getFileGroup(), cf.getFileName(), cf, new HashMap<>());
registerPolarisConfigPublishChangeListener(p);
customRegisterPolarisConfigPublishChangeListener(p);
}
}
else {
registerPolarisConfigPublishChangeListener(polarisPropertySource);
customRegisterPolarisConfigPublishChangeListener(polarisPropertySource);
}
}
}
private void customInitRegisterPolarisConfig(PolarisConfigPropertyAutoRefresher polarisConfigPropertyAutoRefresher) {
if (polarisConfigCustomExtensionLayer == null) {
@ -96,6 +106,7 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
}
public void registerPolarisConfigPublishChangeListener(PolarisPropertySource polarisPropertySource) {
LOGGER.info("{} will register polaris config publish listener", polarisPropertySource.getPropertySourceName());
polarisPropertySource.getConfigKVFile()
.addChangeListener((ConfigKVFileChangeListener) configKVFileChangeEvent -> {
@ -144,4 +155,12 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
}
polarisConfigCustomExtensionLayer.executeRegisterPublishChangeListener(polarisPropertySource);
}
/**
* Just for junit test.
* @param registered if the polaris config property auto refresh is registered
*/
public void setRegistered(boolean registered) {
this.registered.set(registered);
}
}

@ -20,6 +20,7 @@ package com.tencent.cloud.polaris.config.adapter;
import java.util.Map;
import com.tencent.cloud.polaris.config.utils.PolarisPropertySourceUtils;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import org.springframework.core.env.MapPropertySource;
@ -40,7 +41,7 @@ public class PolarisPropertySource extends MapPropertySource {
private final ConfigKVFile configKVFile;
public PolarisPropertySource(String namespace, String group, String fileName, ConfigKVFile configKVFile, Map<String, Object> source) {
super(namespace + "-" + group + "-" + fileName, source);
super(PolarisPropertySourceUtils.generateName(namespace, group, fileName), source);
this.namespace = namespace;
this.group = group;
@ -64,7 +65,7 @@ public class PolarisPropertySource extends MapPropertySource {
return namespace + "-" + group + "-" + fileName;
}
ConfigKVFile getConfigKVFile() {
public ConfigKVFile getConfigKVFile() {
return configKVFile;
}

@ -28,15 +28,25 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @author lepdou 2022-03-28
*/
public class PolarisPropertySourceManager {
public final class PolarisPropertySourceManager {
private final Map<String, PolarisPropertySource> polarisPropertySources = new ConcurrentHashMap<>();
private static final Map<String, PolarisPropertySource> polarisPropertySources = new ConcurrentHashMap<>();
public void addPropertySource(PolarisPropertySource polarisPropertySource) {
polarisPropertySources.putIfAbsent(polarisPropertySource.getPropertySourceName(), polarisPropertySource);
private PolarisPropertySourceManager() {
}
public List<PolarisPropertySource> getAllPropertySources() {
public static void addPropertySource(PolarisPropertySource polarisPropertySource) {
polarisPropertySources.put(polarisPropertySource.getPropertySourceName(), polarisPropertySource);
}
public static List<PolarisPropertySource> getAllPropertySources() {
return new ArrayList<>(polarisPropertySources.values());
}
/**
* Just for test.
*/
public static void clearPropertySources() {
polarisPropertySources.clear();
}
}

@ -59,9 +59,8 @@ public class PolarisRefreshAffectedContextRefresher extends PolarisConfigPropert
private TypeConverter typeConverter;
public PolarisRefreshAffectedContextRefresher(PolarisConfigProperties polarisConfigProperties,
PolarisPropertySourceManager polarisPropertySourceManager, SpringValueRegistry springValueRegistry,
PlaceholderHelper placeholderHelper) {
super(polarisConfigProperties, polarisPropertySourceManager);
SpringValueRegistry springValueRegistry, PlaceholderHelper placeholderHelper) {
super(polarisConfigProperties);
this.springValueRegistry = springValueRegistry;
this.placeholderHelper = placeholderHelper;
}
@ -100,7 +99,7 @@ public class PolarisRefreshAffectedContextRefresher extends PolarisConfigPropert
* Logic transplanted from DefaultListableBeanFactory.
*
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor,
* java.lang.String, java.util.Set, org.springframework.beans.TypeConverter)
* String, Set, TypeConverter)
*/
private Object resolvePropertyValue(SpringValue springValue) {
// value will never be null

@ -35,12 +35,16 @@ public class PolarisRefreshEntireContextRefresher extends PolarisConfigPropertyA
private final ContextRefresher contextRefresher;
public PolarisRefreshEntireContextRefresher(PolarisConfigProperties polarisConfigProperties,
PolarisPropertySourceManager polarisPropertySourceManager,
ContextRefresher contextRefresher) {
super(polarisConfigProperties, polarisPropertySourceManager);
super(polarisConfigProperties);
this.contextRefresher = contextRefresher;
}
@Override
public void refreshSpringValue(String changedKey) {
// do nothing,all config will be refreshed by contextRefresher.refresh
}
@Override
public void refreshConfigurationProperties(Set<String> changeKeys) {
contextRefresher.refresh();

@ -30,19 +30,24 @@ import org.slf4j.LoggerFactory;
public final class PolarisServiceLoaderUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisServiceLoaderUtil.class);
private PolarisServiceLoaderUtil() {
}
// this class provides customized logic for some customers to configure special business group files
private static PolarisConfigCustomExtensionLayer polarisConfigCustomExtensionLayer;
static {
ServiceLoader<PolarisConfigCustomExtensionLayer> polarisConfigCustomExtensionLayerLoader = ServiceLoader.load(PolarisConfigCustomExtensionLayer.class);
Iterator<PolarisConfigCustomExtensionLayer> polarisConfigCustomExtensionLayerIterator = polarisConfigCustomExtensionLayerLoader.iterator();
// Generally, there is only one implementation class. If there are multiple, the last one is loaded
while (polarisConfigCustomExtensionLayerIterator.hasNext()) {
polarisConfigCustomExtensionLayer = polarisConfigCustomExtensionLayerIterator.next();
PolarisConfigCustomExtensionLayer temp = polarisConfigCustomExtensionLayerIterator.next();
if (temp.isEnabled()) {
polarisConfigCustomExtensionLayer = temp;
LOGGER.info("[SCT Config] PolarisConfigFileLocator init polarisConfigCustomExtensionLayer:{}", polarisConfigCustomExtensionLayer);
}
}
}
private PolarisServiceLoaderUtil() {
}
public static PolarisConfigCustomExtensionLayer getPolarisConfigCustomExtensionLayer() {
return polarisConfigCustomExtensionLayer;

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

@ -15,7 +15,6 @@
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.config;
import java.util.List;
@ -27,6 +26,8 @@ import java.util.List;
*/
public class ConfigFileGroup {
private String namespace;
/**
* group name.
*/
@ -37,6 +38,14 @@ public class ConfigFileGroup {
*/
private List<String> files;
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getName() {
return name;
}
@ -55,6 +64,10 @@ public class ConfigFileGroup {
@Override
public String toString() {
return "ConfigFileGroup{" + "name='" + name + '\'' + ", file=" + files + '}';
return "ConfigFileGroup{" +
"namespace='" + namespace + '\'' +
", name='" + name + '\'' +
", files=" + files +
'}';
}
}

@ -50,6 +50,8 @@ public class PolarisConfigProperties {
@Value("${spring.cloud.polaris.config.port:#{'8093'}}")
private int port = 8093;
private String token;
/**
* Whether to automatically update to the spring context when the configuration file.
* is updated
@ -89,6 +91,11 @@ public class PolarisConfigProperties {
*/
private String localFileRootPath = "./polaris/backup/config";
/**
* If internal config file enabled.
*/
private boolean internalEnabled = true;
public boolean isEnabled() {
return enabled;
}
@ -113,6 +120,14 @@ public class PolarisConfigProperties {
this.port = port;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public boolean isAutoRefresh() {
return autoRefresh;
}
@ -168,4 +183,30 @@ public class PolarisConfigProperties {
public void setLocalFileRootPath(String localFileRootPath) {
this.localFileRootPath = localFileRootPath;
}
public boolean isInternalEnabled() {
return internalEnabled;
}
public void setInternalEnabled(boolean internalEnabled) {
this.internalEnabled = internalEnabled;
}
@Override
public String toString() {
return "PolarisConfigProperties{" +
"enabled=" + enabled +
", address='" + address + '\'' +
", port=" + port +
", token='" + token + '\'' +
", autoRefresh=" + autoRefresh +
", shutdownIfConnectToConfigServerFailed=" + shutdownIfConnectToConfigServerFailed +
", preference=" + preference +
", refreshType=" + refreshType +
", groups=" + groups +
", dataSource='" + dataSource + '\'' +
", localFileRootPath='" + localFileRootPath + '\'' +
", internalEnabled=" + internalEnabled +
'}';
}
}

@ -33,15 +33,13 @@ import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
*
* @author shuiqingliu
**/
@Endpoint(id = "polaris-config")
@Endpoint(id = "polarisconfig")
public class PolarisConfigEndpoint {
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisPropertySourceManager polarisPropertySourceManager;
public PolarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) {
public PolarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisPropertySourceManager = polarisPropertySourceManager;
}
@ReadOperation
@ -49,7 +47,7 @@ public class PolarisConfigEndpoint {
Map<String, Object> configInfo = new HashMap<>();
configInfo.put("PolarisConfigProperties", polarisConfigProperties);
List<PolarisPropertySource> propertySourceList = polarisPropertySourceManager.getAllPropertySources();
List<PolarisPropertySource> propertySourceList = PolarisPropertySourceManager.getAllPropertySources();
configInfo.put("PolarisPropertySource", propertySourceList);
return configInfo;

@ -18,7 +18,6 @@
package com.tencent.cloud.polaris.config.endpoint;
import com.tencent.cloud.polaris.config.ConditionalOnPolarisConfigEnabled;
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;
@ -41,8 +40,7 @@ public class PolarisConfigEndpointAutoConfiguration {
@Bean
@ConditionalOnAvailableEndpoint
@ConditionalOnMissingBean
public PolarisConfigEndpoint polarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties,
PolarisPropertySourceManager polarisPropertySourceManager) {
return new PolarisConfigEndpoint(polarisConfigProperties, polarisPropertySourceManager);
public PolarisConfigEndpoint polarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties) {
return new PolarisConfigEndpoint(polarisConfigProperties);
}
}

@ -66,7 +66,6 @@ public final class PolarisConfigChangeEventListener implements ApplicationListen
*/
@Override
public void onApplicationEvent(@NonNull ApplicationEvent event) {
// Initialize application all environment properties .
if (event instanceof ApplicationStartedEvent && started.compareAndSet(false, true)) {
ApplicationStartedEvent applicationStartedEvent = (ApplicationStartedEvent) event;

@ -52,11 +52,9 @@ import static com.tencent.polaris.configuration.api.core.ChangeType.MODIFIED;
* <p>This source file was reference from:
* <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>
*
* @author Palmer Xu 2022-06-06
*/
public final class PolarisConfigListenerContext {
/**
* Logger instance.
*/

@ -21,7 +21,6 @@ package com.tencent.cloud.polaris.config.listener;
import java.util.Collections;
import com.tencent.cloud.polaris.config.adapter.PolarisConfigRefreshScopeAnnotationDetector;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.enums.RefreshType;
@ -44,7 +43,7 @@ import org.springframework.lang.NonNull;
import static com.tencent.cloud.polaris.config.condition.ReflectRefreshTypeCondition.POLARIS_CONFIG_REFRESH_TYPE;
/**
* When {@link com.tencent.cloud.polaris.config.adapter.PolarisConfigRefreshScopeAnnotationDetector} detects that
* When {@link PolarisConfigRefreshScopeAnnotationDetector} detects that
* the annotation {@code @RefreshScope} exists and is used, but the config refresh type
* {@code spring.cloud.polaris.config.refresh-type} is still {@code RefreshType.REFLECT}, then the framework will
* automatically switch the config refresh type to {@code RefreshType.REFRESH_CONTEXT}.
@ -122,12 +121,10 @@ public class PolarisConfigRefreshOptimizationListener implements ApplicationList
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(PolarisRefreshEntireContextRefresher.class);
PolarisConfigProperties polarisConfigProperties = beanFactory.getBean(PolarisConfigProperties.class);
PolarisPropertySourceManager polarisPropertySourceManager = beanFactory.getBean(PolarisPropertySourceManager.class);
ContextRefresher contextRefresher = beanFactory.getBean(ContextRefresher.class);
ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
constructorArgumentValues.addIndexedArgumentValue(0, polarisConfigProperties);
constructorArgumentValues.addIndexedArgumentValue(1, polarisPropertySourceManager);
constructorArgumentValues.addIndexedArgumentValue(2, contextRefresher);
constructorArgumentValues.addIndexedArgumentValue(1, contextRefresher);
beanFactory.registerBeanDefinition(REFRESH_CONTEXT_REFRESHER_BEAN_NAME, beanDefinition);
}

@ -32,7 +32,7 @@ import org.springframework.context.ApplicationListener;
public class PolarisConfigLoggerApplicationListener implements ApplicationListener<ApplicationEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigLoggerApplicationListener.class);
/**
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
* @see ApplicationListener#onApplicationEvent(ApplicationEvent)
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {

@ -13,7 +13,6 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.spring.annotation;

@ -0,0 +1,59 @@
/*
* 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.tsf;
import com.tencent.cloud.common.tsf.ConditionalOnTsfConsulEnabled;
import com.tencent.cloud.polaris.config.ConditionalOnPolarisConfigEnabled;
import com.tencent.cloud.polaris.config.tsf.controller.PolarisAdaptorTsfConfigController;
import com.tencent.tsf.consul.config.watch.TsfConsulConfigRefreshEventListener;
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;
/**
* @author juanyinyang
* @Date Jul 23, 2023 3:52:48 PM
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnTsfConsulEnabled
@ConditionalOnPolarisConfigEnabled
public class PolarisAdaptorTsfConfigAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled", matchIfMissing = true)
public TsfConsulConfigRefreshEventListener polarisAdaptorTsfConsulRefreshEventListener() {
return new TsfConsulConfigRefreshEventListener();
}
/**
*
* 1Spring Cloud Consul ConfigConsul ConfigtsfConfigController
* 2@ConditionalOnPolarisConfigEnabled
* 3tsf.config.instance.released-config.lookup.enabled.
* @return polarisAdaptorTsfConfigController
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "tsf.config.instance.released-config.lookup.enabled", matchIfMissing = true)
public PolarisAdaptorTsfConfigController polarisAdaptorTsfConfigController() {
return new PolarisAdaptorTsfConfigController();
}
}

@ -0,0 +1,71 @@
/*
* 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.tsf.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author juanyinyang
* @Date 202382 5:08:29
*/
@RestController
public class PolarisAdaptorTsfConfigController {
private static final Logger LOG = LoggerFactory.getLogger(PolarisAdaptorTsfConfigController.class);
@Autowired
Environment environment;
public PolarisAdaptorTsfConfigController() {
LOG.info("init PolarisAdaptorTsfConfigController");
}
/**
* TSFSDK.
* @return config map
*/
@RequestMapping("/tsf/innerApi/config/findAllConfig")
public Map<String, Object> findAllConfig() {
List<PolarisPropertySource> propertySourceList = PolarisPropertySourceManager.getAllPropertySources();
Set<String> keys = new HashSet<>();
for (PolarisPropertySource propertySource : propertySourceList) {
keys.addAll(Arrays.asList(propertySource.getPropertyNames()));
}
return keys.stream()
.collect(HashMap::new, (map, key) -> map.put(key, environment.getProperty(key)), HashMap::putAll);
}
}

@ -0,0 +1,48 @@
/*
* 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.tsf.encrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConfigEncryptAESProvider extends ConfigEncryptProvider {
private static final Logger log = LoggerFactory.getLogger(ConfigEncryptAESProvider.class);
@Override
public String encrypt(String content, String password) {
try {
return EncryptAlgorithm.AES256.encrypt(content, password);
}
catch (Exception e) {
log.error("Error on encrypting.", e);
throw e;
}
}
@Override
public String decrypt(String encryptedContent, String password) {
try {
return EncryptAlgorithm.AES256.decrypt(encryptedContent, password);
}
catch (Exception e) {
log.error("Error on decrypting.", e);
throw e;
}
}
}

@ -15,27 +15,30 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.spi;
package com.tencent.cloud.polaris.config.tsf.encrypt;
import java.util.Map;
import java.util.Set;
import feign.RequestTemplate;
import org.springframework.core.Ordered;
/**
* TSF .
*
* @author hongweizhu
*/
public abstract class ConfigEncryptProvider {
/**
* Router label resolver for feign request.
* @author lepdou 2022-07-20
* .
*
* @param content
* @param password
* @return
*/
public interface FeignRouterLabelResolver extends Ordered {
public abstract String encrypt(String content, String password);
/**
* Resolve labels from feign request. User can customize expression parser to extract labels.
* .
*
* @param requestTemplate the feign request.
* @param expressionLabelKeys the expression labels which are configured in router rule.
* @return resolved labels
* @param encryptedContent
* @param password
* @return
*/
Map<String, String> resolve(RequestTemplate requestTemplate, Set<String> expressionLabelKeys);
public abstract String decrypt(String encryptedContent, String password);
}

@ -0,0 +1,39 @@
/*
* 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.tsf.encrypt;
public final class ConfigEncryptProviderFactory {
private static ConfigEncryptProvider configEncryptProvider = null;
private ConfigEncryptProviderFactory() {
}
public static ConfigEncryptProvider getInstance() {
if (null == configEncryptProvider) {
try {
Class<?> providerClass = Class.forName(EncryptConfig.getProviderClass());
configEncryptProvider = (ConfigEncryptProvider) providerClass.newInstance();
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
return configEncryptProvider;
}
}

@ -0,0 +1,146 @@
/*
* 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.tsf.encrypt;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
public class EncryptAlgorithm {
public static class AES256 {
/**
* .
*
* @param content
* @param password
* @return
*/
public static final String encrypt(String content, String password) {
if (null == password || "".equals(password)) {
throw new PasswordNotFoundException();
}
try {
// AES SK生成器
KeyGenerator kgen = KeyGenerator.getInstance("AES");
// SHA-256摘要密钥后生成安全随机数
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(SHA256.encode(password));
kgen.init(256, sr);
// 生成秘密(对称)密钥
SecretKey secretKey = kgen.generateKey();
// 返回基本编码格式的密钥
byte[] enCodeFormat = secretKey.getEncoded();
// 根据给定的字节数组构造一个密钥。enCodeFormat密钥内容"AES":与给定的密钥内容相关联的密钥算法的名称
SecretKeySpec skSpec = new SecretKeySpec(enCodeFormat, "AES");
// 将提供程序添加到下一个可用位置
Security.addProvider(new BouncyCastleProvider());
// 创建一个实现指定转换的 Cipher对象该转换由指定的提供程序提供。
// "AES/ECB/PKCS7Padding":转换的名称;"BC":提供程序的名称
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// 初始化cipher加密模式
cipher.init(Cipher.ENCRYPT_MODE, skSpec);
byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
byte[] cryptograph = cipher.doFinal(byteContent);
byte[] enryptedContent = Base64.encode(cryptograph);
return new String(enryptedContent);
}
catch (Exception e) {
throw new RuntimeException("Failed encrypt.", e);
}
}
/**
* .
*
* @param encryptedContent
* @param password
* @return
*/
public static final String decrypt(String encryptedContent, String password) {
if (null == password || "".equals(password)) {
throw new PasswordNotFoundException();
}
try {
// AES SK生成器
KeyGenerator kgen = KeyGenerator.getInstance("AES");
// SHA-256摘要密钥后生成安全随机数
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(SHA256.encode(password));
kgen.init(256, sr);
// 生成秘密(对称)密钥
SecretKey secretKey = kgen.generateKey();
// 返回基本编码格式的密钥
byte[] enCodeFormat = secretKey.getEncoded();
// 根据给定的字节数组构造一个密钥。enCodeFormat密钥内容"AES":与给定的密钥内容相关联的密钥算法的名称
SecretKeySpec skSpec = new SecretKeySpec(enCodeFormat, "AES");
// 将提供程序添加到下一个可用位置
Security.addProvider(new BouncyCastleProvider());
// 创建一个实现指定转换的 Cipher对象该转换由指定的提供程序提供。
// "AES/ECB/PKCS7Padding":转换的名称;"BC":提供程序的名称
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
// 初始化cipher解密模式
cipher.init(Cipher.DECRYPT_MODE, skSpec);
byte[] result = cipher.doFinal(Base64.decode(encryptedContent.getBytes(StandardCharsets.UTF_8)));
return new String(result);
}
catch (Exception e) {
throw new RuntimeException("Failed decrypt.", e);
}
}
}
public static class SHA256 {
/**
* SHA-256.
*
* @param content
* @return
* @throws NoSuchAlgorithmException
*/
public static byte[] encode(String content) throws NoSuchAlgorithmException {
MessageDigest digester = MessageDigest.getInstance("SHA-256");
digester.update(content.getBytes(StandardCharsets.UTF_8));
return digester.digest();
}
}
public static class PasswordNotFoundException extends RuntimeException {
/**
* serialVersionUID.
*/
private static final long serialVersionUID = -2843758461182470411L;
public PasswordNotFoundException() {
super("Password not found.");
}
}
}

@ -0,0 +1,115 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.config.tsf.encrypt;
import org.springframework.util.StringUtils;
public final class EncryptConfig {
private static final String PASSWORD_KEY = "tsf_config_encrypt_password";
/**
* .
*/
public static String ENCRYPT_PREFIX = "ENC(";
/**
* .
*/
public static String ENCRYPT_SUFFIX = ")";
/**
* .
*/
private static String password;
/**
* .
*/
private static String providerClass = "com.tencent.cloud.polaris.config.tsf.encrypt.ConfigEncryptAESProvider";
static {
// 环境变量
if (null != System.getenv(PASSWORD_KEY)) {
password = System.getenv(PASSWORD_KEY);
}
// JVM参数
if (null != System.getProperty(PASSWORD_KEY)) {
password = System.getProperty(PASSWORD_KEY);
}
}
private EncryptConfig() {
}
/**
* password .
* @return truefalse
*/
public static Boolean getEnabled() {
return !StringUtils.isEmpty(password);
}
public static String getPassword() {
return EncryptConfig.password;
}
public static void setPassword(String password) {
EncryptConfig.password = password;
}
public static ConfigEncryptProvider getProvider() {
return ConfigEncryptProviderFactory.getInstance();
}
public static String getProviderClass() {
return providerClass;
}
public static void setProviderClass(String providerClass) {
EncryptConfig.providerClass = providerClass;
}
/**
* .
*
* @param content
* @return truefalse
*/
public static Boolean needDecrypt(Object content) {
if (null == content) {
return false;
}
else {
String stringValue = String.valueOf(content);
return stringValue.startsWith(ENCRYPT_PREFIX) && stringValue.endsWith(ENCRYPT_SUFFIX);
}
}
/**
* .
*
* @param content
* @return
*/
public static String realContent(Object content) {
if (null != content) {
String stringValue = String.valueOf(content);
return stringValue.substring(ENCRYPT_PREFIX.length(), stringValue.length() - ENCRYPT_SUFFIX.length());
}
return null;
}
}

@ -0,0 +1,34 @@
/*
* 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.utils;
/**
* Utils for PolarisPropertySource.
*
* @author Haotian Zhang
*/
public final class PolarisPropertySourceUtils {
private PolarisPropertySourceUtils() {
}
public static String generateName(String namespace, String group, String fileName) {
return namespace + "-" + group + "-" + fileName;
}
}

@ -0,0 +1,28 @@
/*
* 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.tsf.consul.config.watch;
public interface ConfigChangeCallback {
/**
* .
* @param lastConfigProperty
* @param newConfigProperty
*/
void callback(ConfigProperty lastConfigProperty, ConfigProperty newConfigProperty);
}

@ -0,0 +1,40 @@
/*
* 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.tsf.consul.config.watch;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ConfigChangeListener {
String prefix() default "";
String[] value() default {};
boolean async() default false;
}

@ -0,0 +1,46 @@
/*
* 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.tsf.consul.config.watch;
public class ConfigProperty {
private String key;
private Object value;
public ConfigProperty(String key, Object value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}

@ -0,0 +1,122 @@
/*
* 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.tsf.consul.config.watch;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
import com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext;
import com.tencent.cloud.polaris.config.listener.SyncConfigChangeListener;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.lang.NonNull;
import org.springframework.util.StringUtils;
public class TsfConsulConfigRefreshEventListener implements BeanPostProcessor, PriorityOrdered {
private static final String DOT = ".";
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public Object postProcessBeforeInitialization(@NonNull Object obj, @NonNull String beanName) throws BeansException {
return obj;
}
@Override
public Object postProcessAfterInitialization(@NonNull Object obj, @NonNull String beanName) throws BeansException {
Class<?> clz = obj.getClass();
if (!clz.isAnnotationPresent(ConfigChangeListener.class) || !ConfigChangeCallback.class.isAssignableFrom(clz)) {
return obj;
}
ConfigChangeListener targetAnno = clz.getAnnotation(ConfigChangeListener.class);
String watchedPrefix = targetAnno.prefix();
String[] watchedConfirmedValue = targetAnno.value();
boolean isAsync = targetAnno.async();
if (watchedConfirmedValue.length == 0 && StringUtils.isEmpty(watchedPrefix)) {
return obj;
}
ConfigChangeCallback bean = (ConfigChangeCallback) obj;
com.tencent.cloud.polaris.config.listener.ConfigChangeListener listener = new SyncConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
List<TsfCallbackParam> paramList = parseConfigChangeEventToTsfCallbackParam(changeEvent);
for (TsfCallbackParam param : paramList) {
if (isAsync()) {
PolarisConfigListenerContext.executor()
.execute(() -> bean.callback(param.oldValue, param.newValue));
}
else {
bean.callback(param.oldValue, param.newValue);
}
}
}
@Override
public boolean isAsync() {
return isAsync;
}
};
Set<String> interestedKeys = new HashSet<>();
Set<String> interestedKeyPrefixes = new HashSet<>();
if (watchedConfirmedValue.length > 0) {
for (String value : watchedConfirmedValue) {
interestedKeys.add(StringUtils.isEmpty(watchedPrefix) ? value : watchedPrefix + DOT + value);
}
}
else {
interestedKeyPrefixes.add(watchedPrefix);
}
PolarisConfigListenerContext.addChangeListener(listener, interestedKeys, interestedKeyPrefixes);
return bean;
}
private List<TsfCallbackParam> parseConfigChangeEventToTsfCallbackParam(ConfigChangeEvent event) {
List<TsfCallbackParam> result = new ArrayList<>();
Set<String> changedKeys = event.changedKeys();
for (String changedKey : changedKeys) {
ConfigProperty oldValue = new ConfigProperty(changedKey, event.getChange(changedKey).getOldValue());
ConfigProperty newValue = new ConfigProperty(changedKey, event.getChange(changedKey).getNewValue());
TsfCallbackParam param = new TsfCallbackParam(oldValue, newValue);
result.add(param);
}
return result;
}
static class TsfCallbackParam {
ConfigProperty oldValue;
ConfigProperty newValue;
TsfCallbackParam(ConfigProperty oldValue, ConfigProperty newValue) {
this.oldValue = oldValue;
this.newValue = newValue;
}
}
}

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

@ -29,6 +29,7 @@ import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@ -42,7 +43,6 @@ import static org.mockito.Mockito.when;
/**
* test for {@link PolarisConfigFileLocator}.
*
*@author lepdou 2022-06-11
*/
@ExtendWith(MockitoExtension.class)
@ -57,14 +57,17 @@ public class PolarisConfigFileLocatorTest {
@Mock
private ConfigFileService configFileService;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
@Mock
private Environment environment;
@BeforeEach
public void setUp() {
PolarisPropertySourceManager.clearPropertySources();
}
@Test
public void testLoadApplicationPropertiesFile() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, polarisPropertySourceManager, environment);
configFileService, environment);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
@ -86,7 +89,9 @@ public class PolarisConfigFileLocatorTest {
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yaml")).thenReturn(emptyConfigFile);
when(polarisConfigProperties.isEnabled()).thenReturn(true);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(polarisConfigProperties.isInternalEnabled()).thenReturn(true);
when(environment.getActiveProfiles()).thenReturn(new String[] {});
PropertySource<?> propertySource = locator.locate(environment);
@ -99,7 +104,7 @@ public class PolarisConfigFileLocatorTest {
@Test
public void testActiveProfileFilesPriorityBiggerThanDefault() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, polarisPropertySourceManager, environment);
configFileService, environment);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
@ -133,7 +138,9 @@ public class PolarisConfigFileLocatorTest {
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap-dev.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap-dev.yaml")).thenReturn(emptyConfigFile);
when(polarisConfigProperties.isEnabled()).thenReturn(true);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(polarisConfigProperties.isInternalEnabled()).thenReturn(true);
when(environment.getActiveProfiles()).thenReturn(new String[] {"dev"});
PropertySource<?> propertySource = locator.locate(environment);
@ -146,7 +153,7 @@ public class PolarisConfigFileLocatorTest {
@Test
public void testGetCustomFiles() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, polarisPropertySourceManager, environment);
configFileService, environment);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
@ -170,7 +177,9 @@ public class PolarisConfigFileLocatorTest {
configFileGroup.setFiles(Lists.newArrayList(customFile1, customFile2));
customFiles.add(configFileGroup);
when(polarisConfigProperties.isEnabled()).thenReturn(true);
when(polarisConfigProperties.getGroups()).thenReturn(customFiles);
when(polarisConfigProperties.isInternalEnabled()).thenReturn(true);
when(environment.getActiveProfiles()).thenReturn(new String[] {});
// file1.properties

@ -24,7 +24,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.Lists;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper;
import com.tencent.cloud.polaris.config.spring.property.SpringValue;
@ -32,6 +31,7 @@ import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.polaris.configuration.api.core.ChangeType;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@ -59,8 +59,6 @@ public class PolarisPropertiesSourceAutoRefresherTest {
private final String testFileName = "application.properties";
@Mock
private PolarisConfigProperties polarisConfigProperties;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
@Mock
private SpringValueRegistry springValueRegistry;
@ -68,10 +66,14 @@ public class PolarisPropertiesSourceAutoRefresherTest {
@Mock
private PlaceholderHelper placeholderHelper;
@BeforeEach
public void setUp() {
PolarisPropertySourceManager.clearPropertySources();
}
@Test
public void testConfigFileChanged() throws Exception {
PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(polarisConfigProperties,
polarisPropertySourceManager, springValueRegistry, placeholderHelper);
PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(polarisConfigProperties, springValueRegistry, placeholderHelper);
ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class);
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
TypeConverter typeConverter = mock(TypeConverter.class);
@ -99,7 +101,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName,
file, content);
when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED);

@ -24,7 +24,6 @@ import java.util.Objects;
import com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration;
import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.adapter.PolarisRefreshAffectedContextRefresher;
import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
@ -107,7 +106,6 @@ public class ConditionalOnReflectRefreshTypeTest {
.withPropertyValues("spring.cloud.polaris.config.enabled=true");
contextRunner.run(context -> {
assertThat(context).hasSingleBean(PolarisConfigProperties.class);
assertThat(context).hasSingleBean(PolarisPropertySourceManager.class);
assertThat(context).hasSingleBean(ContextRefresher.class);
assertThat(context).hasSingleBean(PolarisRefreshEntireContextRefresher.class);
});

@ -26,13 +26,13 @@ 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.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Test for polaris config endpoint.
@ -48,8 +48,11 @@ public class PolarisConfigEndpointTest {
@Mock
private PolarisConfigProperties polarisConfigProperties;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
@BeforeEach
public void setUp() {
PolarisPropertySourceManager.clearPropertySources();
}
@Test
public void testPolarisConfigEndpoint() {
@ -60,9 +63,9 @@ public class PolarisConfigEndpointTest {
MockedConfigKVFile file = new MockedConfigKVFile(content);
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName,
file, content);
when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
PolarisConfigEndpoint endpoint = new PolarisConfigEndpoint(polarisConfigProperties, polarisPropertySourceManager);
PolarisConfigEndpoint endpoint = new PolarisConfigEndpoint(polarisConfigProperties);
Map<String, Object> info = endpoint.polarisConfig();
assertThat(polarisConfigProperties).isEqualTo(info.get("PolarisConfigProperties"));
assertThat(Lists.newArrayList(polarisPropertySource)).isEqualTo(info.get("PolarisPropertySource"));

@ -21,7 +21,6 @@ package com.tencent.cloud.polaris.config.listener;
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;
@ -31,6 +30,7 @@ import com.tencent.cloud.polaris.config.enums.RefreshType;
import com.tencent.polaris.configuration.api.core.ChangeType;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
@ -77,6 +77,11 @@ public class PolarisConfigRefreshOptimizationListenerNotTriggeredTest {
@Autowired
private ConfigurableApplicationContext context;
@BeforeAll
static void beforeAll() {
PolarisPropertySourceManager.clearPropertySources();
}
@Test
public void testNotSwitchConfigRefreshType() {
RefreshType actualRefreshType = context.getEnvironment()
@ -100,12 +105,12 @@ public class PolarisConfigRefreshOptimizationListenerNotTriggeredTest {
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_FILE_NAME,
file, content);
PolarisPropertySourceManager manager = context.getBean(PolarisPropertySourceManager.class);
when(manager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
PolarisRefreshAffectedContextRefresher refresher = context.getBean(PolarisRefreshAffectedContextRefresher.class);
PolarisRefreshAffectedContextRefresher spyRefresher = Mockito.spy(refresher);
refresher.setRegistered(false);
spyRefresher.onApplicationEvent(null);
ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
@ -134,12 +139,6 @@ public class PolarisConfigRefreshOptimizationListenerNotTriggeredTest {
@SpringBootApplication
protected static class TestApplication {
@Primary
@Bean
public PolarisPropertySourceManager polarisPropertySourceManager() {
return mock(PolarisPropertySourceManager.class);
}
@Primary
@Bean
public ContextRefresher contextRefresher() {

@ -21,7 +21,6 @@ package com.tencent.cloud.polaris.config.listener;
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;
@ -31,6 +30,7 @@ import com.tencent.cloud.polaris.config.enums.RefreshType;
import com.tencent.polaris.configuration.api.core.ChangeType;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
@ -78,6 +78,11 @@ public class PolarisConfigRefreshOptimizationListenerTriggeredTest {
@Autowired
private ConfigurableApplicationContext context;
@BeforeEach
public void setUp() {
PolarisPropertySourceManager.clearPropertySources();
}
@Test
public void testSwitchConfigRefreshType() {
RefreshType actualRefreshType = context.getEnvironment()
@ -101,12 +106,12 @@ public class PolarisConfigRefreshOptimizationListenerTriggeredTest {
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(TEST_NAMESPACE, TEST_SERVICE_NAME, TEST_FILE_NAME,
file, content);
PolarisPropertySourceManager manager = context.getBean(PolarisPropertySourceManager.class);
when(manager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
PolarisPropertySourceManager.addPropertySource(polarisPropertySource);
PolarisRefreshEntireContextRefresher refresher = context.getBean(PolarisRefreshEntireContextRefresher.class);
PolarisRefreshEntireContextRefresher spyRefresher = Mockito.spy(refresher);
refresher.setRegistered(false);
spyRefresher.onApplicationEvent(null);
ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
@ -135,12 +140,6 @@ public class PolarisConfigRefreshOptimizationListenerTriggeredTest {
@SpringBootApplication
protected static class TestApplication {
@Primary
@Bean
public PolarisPropertySourceManager polarisPropertySourceManager() {
return mock(PolarisPropertySourceManager.class);
}
@Primary
@Bean
public ContextRefresher contextRefresher() {

@ -36,28 +36,13 @@
<!-- Spring cloud dependencies start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<exclusions>
<exclusion>
<artifactId>swagger-models</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
<exclusion>
<artifactId>swagger-annotations</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
</exclusions>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
</dependency>
<dependency>

@ -17,27 +17,33 @@
package com.tencent.cloud.polaris.contract;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tencent.cloud.common.util.JacksonUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencent.cloud.common.util.GzipUtil;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.plugin.server.InterfaceDescriptor;
import com.tencent.polaris.api.plugin.server.ReportServiceContractRequest;
import com.tencent.polaris.api.plugin.server.ReportServiceContractResponse;
import io.swagger.models.HttpMethod;
import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Swagger;
import com.tencent.polaris.api.utils.StringUtils;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import springfox.documentation.service.Documentation;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import org.springdoc.api.AbstractOpenApiResource;
import org.springdoc.api.AbstractOpenApiResourceUtil;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.webflux.api.OpenApiWebFluxUtil;
import org.springdoc.webmvc.api.OpenApiWebMvcUtil;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
@ -52,50 +58,79 @@ import org.springframework.util.CollectionUtils;
public class PolarisContractReporter implements ApplicationListener<ApplicationReadyEvent> {
private final Logger LOG = LoggerFactory.getLogger(PolarisContractReporter.class);
private final ServiceModelToSwagger2Mapper swagger2Mapper;
private final DocumentationCache documentationCache;
private final org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource;
private final org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource;
private final PolarisContractProperties polarisContractProperties;
private final ProviderAPI providerAPI;
private final PolarisDiscoveryProperties polarisDiscoveryProperties;
public PolarisContractReporter(DocumentationCache documentationCache, ServiceModelToSwagger2Mapper swagger2Mapper,
PolarisContractProperties polarisContractProperties, ProviderAPI providerAPI, PolarisDiscoveryProperties polarisDiscoveryProperties) {
this.swagger2Mapper = swagger2Mapper;
this.documentationCache = documentationCache;
private final ObjectMapperProvider springdocObjectMapperProvider;
public PolarisContractReporter(org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource,
org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource,
PolarisContractProperties polarisContractProperties, ProviderAPI providerAPI,
PolarisDiscoveryProperties polarisDiscoveryProperties, ObjectMapperProvider springdocObjectMapperProvider) {
this.multipleOpenApiWebMvcResource = multipleOpenApiWebMvcResource;
this.multipleOpenApiWebFluxResource = multipleOpenApiWebFluxResource;
this.polarisContractProperties = polarisContractProperties;
this.providerAPI = providerAPI;
this.polarisDiscoveryProperties = polarisDiscoveryProperties;
this.springdocObjectMapperProvider = springdocObjectMapperProvider;
}
@Override
public void onApplicationEvent(@NonNull ApplicationReadyEvent applicationReadyEvent) {
if (polarisContractProperties.isReportEnabled()) {
try {
Documentation documentation = documentationCache.documentationByGroup(polarisContractProperties.getGroup());
Swagger swagger = swagger2Mapper.mapDocumentation(documentation);
if (swagger != null) {
AbstractOpenApiResource openApiResource = null;
if (multipleOpenApiWebMvcResource != null) {
openApiResource = OpenApiWebMvcUtil.getOpenApiResourceOrThrow(multipleOpenApiWebMvcResource, polarisContractProperties.getGroup());
}
else if (multipleOpenApiWebFluxResource != null) {
openApiResource = OpenApiWebFluxUtil.getOpenApiResourceOrThrow(multipleOpenApiWebFluxResource, polarisContractProperties.getGroup());
}
OpenAPI openAPI = null;
if (openApiResource != null) {
openAPI = AbstractOpenApiResourceUtil.getOpenApi(openApiResource);
}
if (openAPI != null) {
ReportServiceContractRequest request = new ReportServiceContractRequest();
request.setName(polarisDiscoveryProperties.getService());
String name = polarisContractProperties.getName();
if (StringUtils.isBlank(name)) {
name = polarisDiscoveryProperties.getService();
}
request.setName(name);
request.setNamespace(polarisDiscoveryProperties.getNamespace());
request.setService(polarisDiscoveryProperties.getService());
request.setProtocol("http");
request.setVersion(polarisDiscoveryProperties.getVersion());
List<InterfaceDescriptor> interfaceDescriptorList = getInterfaceDescriptorFromSwagger(swagger);
List<InterfaceDescriptor> interfaceDescriptorList = getInterfaceDescriptorFromSwagger(openAPI);
request.setInterfaceDescriptors(interfaceDescriptorList);
String jsonValue;
if (springdocObjectMapperProvider != null && springdocObjectMapperProvider.jsonMapper() != null) {
jsonValue = springdocObjectMapperProvider.jsonMapper().writeValueAsString(openAPI);
}
else {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonValue = mapper.writeValueAsString(openAPI);
}
String serviceApiMeta = GzipUtil.compressBase64Encode(jsonValue, "utf-8");
request.setContent(serviceApiMeta);
ReportServiceContractResponse response = providerAPI.reportServiceContract(request);
LOG.info("Service contract [Namespace: {}. Name: {}. Service: {}. Protocol:{}. Version: {}. API counter: {}] is reported.",
request.getNamespace(), request.getName(), request.getService(), request.getProtocol(),
request.getVersion(), request.getInterfaceDescriptors().size());
if (LOG.isDebugEnabled()) {
String jsonValue = JacksonUtils.serialize2Json(swagger);
LOG.debug("OpenApi json data: {}", jsonValue);
LOG.debug("OpenApi json base64 data: {}", serviceApiMeta);
}
}
else {
LOG.warn("Swagger or json is null, documentationCache keys:{}, group:{}", documentationCache.all()
.keySet(), polarisContractProperties.getGroup());
LOG.warn("OpenAPI or json is null, group:{}", polarisContractProperties.getGroup());
}
}
catch (Throwable t) {
@ -104,11 +139,11 @@ public class PolarisContractReporter implements ApplicationListener<ApplicationR
}
}
private List<InterfaceDescriptor> getInterfaceDescriptorFromSwagger(Swagger swagger) {
private List<InterfaceDescriptor> getInterfaceDescriptorFromSwagger(OpenAPI openAPI) {
List<InterfaceDescriptor> interfaceDescriptorList = new ArrayList<>();
Map<String, Path> paths = swagger.getPaths();
for (Map.Entry<String, Path> p : paths.entrySet()) {
Path path = p.getValue();
Paths paths = openAPI.getPaths();
for (Map.Entry<String, PathItem> p : paths.entrySet()) {
PathItem path = p.getValue();
Map<String, Operation> operationMap = getOperationMapFromPath(path);
if (CollectionUtils.isEmpty(operationMap)) {
continue;
@ -117,36 +152,50 @@ public class PolarisContractReporter implements ApplicationListener<ApplicationR
InterfaceDescriptor interfaceDescriptor = new InterfaceDescriptor();
interfaceDescriptor.setPath(p.getKey());
interfaceDescriptor.setMethod(o.getKey());
interfaceDescriptor.setContent(JacksonUtils.serialize2Json(p.getValue()));
try {
String jsonValue;
if (springdocObjectMapperProvider != null && springdocObjectMapperProvider.jsonMapper() != null) {
jsonValue = springdocObjectMapperProvider.jsonMapper().writeValueAsString(o.getValue());
}
else {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonValue = mapper.writeValueAsString(o.getValue());
}
interfaceDescriptor.setContent(GzipUtil.compressBase64Encode(jsonValue, "utf-8"));
}
catch (IOException ioe) {
LOG.warn("Encode operation [{}] failed.", o.getValue(), ioe);
}
interfaceDescriptorList.add(interfaceDescriptor);
}
}
return interfaceDescriptorList;
}
private Map<String, Operation> getOperationMapFromPath(Path path) {
private Map<String, Operation> getOperationMapFromPath(PathItem path) {
Map<String, Operation> operationMap = new HashMap<>();
if (path.getGet() != null) {
operationMap.put(HttpMethod.GET.name(), path.getGet());
operationMap.put(PathItem.HttpMethod.GET.name(), path.getGet());
}
if (path.getPut() != null) {
operationMap.put(HttpMethod.PUT.name(), path.getPut());
operationMap.put(PathItem.HttpMethod.PUT.name(), path.getPut());
}
if (path.getPost() != null) {
operationMap.put(HttpMethod.POST.name(), path.getPost());
operationMap.put(PathItem.HttpMethod.POST.name(), path.getPost());
}
if (path.getHead() != null) {
operationMap.put(HttpMethod.HEAD.name(), path.getHead());
operationMap.put(PathItem.HttpMethod.HEAD.name(), path.getHead());
}
if (path.getDelete() != null) {
operationMap.put(HttpMethod.DELETE.name(), path.getDelete());
operationMap.put(PathItem.HttpMethod.DELETE.name(), path.getDelete());
}
if (path.getPatch() != null) {
operationMap.put(HttpMethod.PATCH.name(), path.getPatch());
operationMap.put(PathItem.HttpMethod.PATCH.name(), path.getPatch());
}
if (path.getOptions() != null) {
operationMap.put(HttpMethod.OPTIONS.name(), path.getOptions());
operationMap.put(PathItem.HttpMethod.OPTIONS.name(), path.getOptions());
}
return operationMap;

@ -51,4 +51,8 @@ public interface ContractProperties {
boolean isReportEnabled();
void setReportEnabled(boolean reportEnabled);
String getName();
void setName(String name);
}

@ -41,7 +41,8 @@ public class PolarisContractModifier implements PolarisConfigModifier {
public void modify(ConfigurationImpl configuration) {
List<RegisterConfigImpl> registerConfigs = configuration.getProvider().getRegisters();
for (RegisterConfigImpl registerConfig : registerConfigs) {
registerConfig.setReportServiceContractEnable(polarisContractProperties.isEnabled());
registerConfig.setReportServiceContractEnable(
polarisContractProperties.isEnabled() && polarisContractProperties.isReportEnabled());
}
}

@ -17,10 +17,6 @@
package com.tencent.cloud.polaris.contract.config;
import java.util.Objects;
import javax.annotation.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -32,8 +28,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("spring.cloud.polaris.contract")
public class PolarisContractProperties implements ContractProperties {
private final ExtendedContractProperties extendContractProperties;
private boolean enabled = true;
/**
* Packages to be scanned. Split by ",".
@ -46,7 +40,7 @@ public class PolarisContractProperties implements ContractProperties {
/**
* Group to create swagger docket.
*/
private String group = "default";
private String group = "polaris";
/**
* Base paths to be scanned. Split by ",".
*/
@ -57,15 +51,10 @@ public class PolarisContractProperties implements ContractProperties {
@Value("${spring.cloud.polaris.contract.report.enabled:true}")
private boolean reportEnabled = true;
public PolarisContractProperties(@Nullable ExtendedContractProperties extendContractProperties) {
this.extendContractProperties = extendContractProperties;
}
private String name;
@Override
public boolean isEnabled() {
if (Objects.nonNull(extendContractProperties)) {
return extendContractProperties.isEnabled();
}
return enabled;
}
@ -76,9 +65,6 @@ public class PolarisContractProperties implements ContractProperties {
@Override
public String getBasePackage() {
if (Objects.nonNull(extendContractProperties)) {
return extendContractProperties.getBasePackage();
}
return basePackage;
}
@ -89,9 +75,6 @@ public class PolarisContractProperties implements ContractProperties {
@Override
public String getExcludePath() {
if (Objects.nonNull(extendContractProperties)) {
return extendContractProperties.getExcludePath();
}
return excludePath;
}
@ -102,9 +85,6 @@ public class PolarisContractProperties implements ContractProperties {
@Override
public String getGroup() {
if (Objects.nonNull(extendContractProperties)) {
return extendContractProperties.getGroup();
}
return group;
}
@ -115,9 +95,6 @@ public class PolarisContractProperties implements ContractProperties {
@Override
public String getBasePath() {
if (Objects.nonNull(extendContractProperties)) {
return extendContractProperties.getBasePath();
}
return basePath;
}
@ -128,9 +105,6 @@ public class PolarisContractProperties implements ContractProperties {
@Override
public boolean isExposure() {
if (Objects.nonNull(extendContractProperties)) {
return extendContractProperties.isExposure();
}
return exposure;
}
@ -148,4 +122,13 @@ public class PolarisContractProperties implements ContractProperties {
public void setReportEnabled(boolean reportEnabled) {
this.reportEnabled = reportEnabled;
}
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}

@ -17,8 +17,6 @@
package com.tencent.cloud.polaris.contract.config;
import javax.annotation.Nullable;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -36,8 +34,8 @@ public class PolarisContractPropertiesAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public PolarisContractProperties polarisContractProperties(@Nullable ExtendedContractProperties extendedContractProperties) {
return new PolarisContractProperties(extendedContractProperties);
public PolarisContractProperties polarisContractProperties() {
return new PolarisContractProperties();
}
@Bean

@ -17,11 +17,6 @@
package com.tencent.cloud.polaris.contract.config;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
@ -30,13 +25,14 @@ import com.tencent.cloud.polaris.contract.PolarisSwaggerApplicationListener;
import com.tencent.cloud.polaris.contract.filter.ApiDocServletFilter;
import com.tencent.cloud.polaris.contract.filter.ApiDocWebFluxFilter;
import com.tencent.cloud.polaris.contract.utils.PackageUtil;
import springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.SpringDocConfiguration;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.webflux.api.MultipleOpenApiWebFluxResource;
import org.springdoc.webmvc.api.MultipleOpenApiWebMvcResource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -45,6 +41,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import static com.tencent.cloud.polaris.contract.utils.PackageUtil.SPLITTER;
/**
* Auto configuration for Polaris swagger.
@ -54,7 +54,7 @@ import org.springframework.context.annotation.Import;
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled
@ConditionalOnProperty(name = "spring.cloud.polaris.contract.enabled", havingValue = "true", matchIfMissing = true)
@Import(OpenApiAutoConfiguration.class)
@Import(SpringDocConfiguration.class)
public class PolarisSwaggerAutoConfiguration {
static {
@ -64,65 +64,45 @@ public class PolarisSwaggerAutoConfiguration {
}
@Bean
public Docket polarisDocket(PolarisContractProperties polarisContractProperties) {
List<Predicate<String>> excludePathList = PackageUtil.getExcludePathPredicates(polarisContractProperties.getExcludePath());
List<Predicate<String>> basePathList = PackageUtil.getBasePathPredicates(polarisContractProperties.getBasePath());
public GroupedOpenApi polarisGroupedOpenApi(PolarisContractProperties polarisContractProperties) {
String basePackage = PackageUtil.scanPackage(polarisContractProperties.getBasePackage());
Predicate<String> basePathListOr = null;
for (Predicate<String> basePathPredicate : basePathList) {
if (basePathListOr == null) {
basePathListOr = basePathPredicate;
}
else {
basePathListOr = basePathListOr.or(basePathPredicate);
}
}
Predicate<String> excludePathListOr = null;
for (Predicate<String> excludePathPredicate : excludePathList) {
if (excludePathListOr == null) {
excludePathListOr = excludePathPredicate;
}
else {
excludePathListOr = excludePathListOr.or(excludePathPredicate);
String[] basePaths = {};
if (StringUtils.hasText(polarisContractProperties.getBasePath())) {
basePaths = polarisContractProperties.getBasePath().split(SPLITTER);
}
String[] excludePaths = {};
if (StringUtils.hasText(polarisContractProperties.getExcludePath())) {
excludePaths = polarisContractProperties.getExcludePath().split(SPLITTER);
}
Predicate<String> pathsPredicate = basePathListOr;
if (excludePathListOr != null) {
excludePathListOr = excludePathListOr.negate();
pathsPredicate = pathsPredicate.and(excludePathListOr);
return GroupedOpenApi.builder()
.packagesToScan(basePackage)
.pathsToMatch(basePaths)
.pathsToExclude(excludePaths)
.group(polarisContractProperties.getGroup())
.build();
}
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(PackageUtil.basePackage(basePackage))
.paths(pathsPredicate)
.build()
.groupName(polarisContractProperties.getGroup())
.enable(polarisContractProperties.isEnabled())
.directModelSubstitute(LocalDate.class, Date.class)
.apiInfo(new ApiInfoBuilder()
.title("Polaris Swagger API")
.description("This is to show polaris api description.")
.license("BSD-3-Clause")
.licenseUrl("https://opensource.org/licenses/BSD-3-Clause")
.termsOfServiceUrl("")
.version("1.0.0")
.contact(new Contact("", "", ""))
.build());
@Bean
@ConditionalOnMissingBean
public OpenAPI polarisOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("Polaris Contract")
.description("This is to show polaris contract description.")
.license(new License().name("BSD-3-Clause").url("https://opensource.org/licenses/BSD-3-Clause"))
.version("1.0.0"));
}
@Bean
@ConditionalOnBean(Docket.class)
@ConditionalOnBean(OpenAPI.class)
@ConditionalOnMissingBean
public PolarisContractReporter polarisContractReporter(DocumentationCache documentationCache,
ServiceModelToSwagger2Mapper swagger2Mapper, PolarisContractProperties polarisContractProperties,
PolarisSDKContextManager polarisSDKContextManager, PolarisDiscoveryProperties polarisDiscoveryProperties) {
return new PolarisContractReporter(documentationCache, swagger2Mapper, polarisContractProperties,
polarisSDKContextManager.getProviderAPI(), polarisDiscoveryProperties);
public PolarisContractReporter polarisContractReporter(
@Nullable MultipleOpenApiWebMvcResource multipleOpenApiWebMvcResource,
@Nullable MultipleOpenApiWebFluxResource multipleOpenApiWebFluxResource,
PolarisContractProperties polarisContractProperties, PolarisSDKContextManager polarisSDKContextManager,
PolarisDiscoveryProperties polarisDiscoveryProperties, ObjectMapperProvider springdocObjectMapperProvider) {
return new PolarisContractReporter(multipleOpenApiWebMvcResource, multipleOpenApiWebFluxResource,
polarisContractProperties, polarisSDKContextManager.getProviderAPI(), polarisDiscoveryProperties, springdocObjectMapperProvider);
}
@Bean

@ -26,7 +26,6 @@ import javax.servlet.http.HttpServletResponse;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
import org.springframework.lang.NonNull;
import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.polaris.contract.filter.FilterConstant.SWAGGER_RESOURCE_PREFIX;
@ -51,11 +50,9 @@ public class ApiDocServletFilter extends OncePerRequestFilter {
}
@Override
public void doFilterInternal(@NonNull HttpServletRequest httpServletRequest,
@NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain)
throws ServletException, IOException {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (!polarisContractProperties.isExposure()) {
String path = httpServletRequest.getServletPath();
String path = request.getServletPath();
if (path.startsWith(SWAGGER_V2_API_DOC_URL) ||
path.startsWith(SWAGGER_V3_API_DOC_URL) ||
path.startsWith(SWAGGER_UI_V2_URL) ||
@ -63,11 +60,11 @@ public class ApiDocServletFilter extends OncePerRequestFilter {
path.startsWith(SWAGGER_RESOURCE_PREFIX) ||
path.startsWith(SWAGGER_WEBJARS_V2_PREFIX) ||
path.startsWith(SWAGGER_WEBJARS_V3_PREFIX)) {
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
filterChain.doFilter(request, response);
}
}

@ -17,13 +17,13 @@
package com.tencent.cloud.polaris.contract.filter;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
@ -42,6 +42,7 @@ import static com.tencent.cloud.polaris.contract.filter.FilterConstant.SWAGGER_W
* @author Haotian Zhang
*/
public class ApiDocWebFluxFilter implements WebFilter {
private final PolarisContractProperties polarisContractProperties;
public ApiDocWebFluxFilter(PolarisContractProperties polarisContractProperties) {
@ -49,7 +50,7 @@ public class ApiDocWebFluxFilter implements WebFilter {
}
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
public Mono<Void> filter(ServerWebExchange serverWebExchange, @NonNull WebFilterChain webFilterChain) {
if (!polarisContractProperties.isExposure()) {
String path = serverWebExchange.getRequest().getURI().getPath();
if (path.startsWith(SWAGGER_V2_API_DOC_URL) ||

@ -0,0 +1,137 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.contract.tsf;
import java.util.concurrent.atomic.AtomicBoolean;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencent.cloud.common.util.GzipUtil;
import io.swagger.v3.oas.models.OpenAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.api.AbstractOpenApiResource;
import org.springdoc.api.AbstractOpenApiResourceUtil;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.webflux.api.OpenApiWebFluxUtil;
import org.springdoc.webmvc.api.OpenApiWebMvcUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.context.SmartLifecycle;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
public class TsfApiMetadataGrapher implements SmartLifecycle {
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource;
private final org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource;
private final ObjectMapperProvider springdocObjectMapperProvider;
private Logger logger = LoggerFactory.getLogger(TsfApiMetadataGrapher.class);
private ApplicationContext applicationContext;
private String groupName;
public TsfApiMetadataGrapher(org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource,
org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource,
String groupName, ApplicationContext applicationContext, ObjectMapperProvider springdocObjectMapperProvider) {
this.applicationContext = applicationContext;
this.multipleOpenApiWebMvcResource = multipleOpenApiWebMvcResource;
this.multipleOpenApiWebFluxResource = multipleOpenApiWebFluxResource;
this.groupName = groupName;
this.springdocObjectMapperProvider = springdocObjectMapperProvider;
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable runnable) {
runnable.run();
stop();
}
@Override
public void start() {
if (!isRunning.compareAndSet(false, true)) {
return;
}
try {
AbstractOpenApiResource openApiResource = null;
if (multipleOpenApiWebMvcResource != null) {
openApiResource = OpenApiWebMvcUtil.getOpenApiResourceOrThrow(multipleOpenApiWebMvcResource, groupName);
}
else if (multipleOpenApiWebFluxResource != null) {
openApiResource = OpenApiWebFluxUtil.getOpenApiResourceOrThrow(multipleOpenApiWebFluxResource, groupName);
}
OpenAPI openAPI = null;
if (openApiResource != null) {
openAPI = AbstractOpenApiResourceUtil.getOpenApi(openApiResource);
}
String jsonValue;
if (springdocObjectMapperProvider != null && springdocObjectMapperProvider.jsonMapper() != null) {
jsonValue = springdocObjectMapperProvider.jsonMapper().writeValueAsString(openAPI);
}
else {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
jsonValue = mapper.writeValueAsString(openAPI);
}
if (openAPI != null && !StringUtils.isEmpty(jsonValue)) {
String serviceApiMeta = GzipUtil.compressBase64Encode(jsonValue, "utf-8");
Environment environment = applicationContext.getEnvironment();
String tsfToken = environment.getProperty("tsf_token");
String tsfGroupId = environment.getProperty("tsf_group_id");
if (StringUtils.isEmpty(tsfGroupId) || StringUtils.isEmpty(tsfToken)) {
logger.info("[tsf-swagger] auto smart check application start with local consul, api registry not work");
return;
}
logger.info("[tsf-swagger] api_meta len: {}", serviceApiMeta.length());
String applicationName = environment.getProperty("spring.application.name");
if (logger.isDebugEnabled()) {
logger.debug("[tsf-swagger] service: {} openApi json data: {}", applicationName, jsonValue);
logger.debug("[tsf-swagger] service: {} api_meta info: {}", applicationName, serviceApiMeta);
}
System.setProperty(String.format("$%s", "api_metas"), serviceApiMeta);
}
else {
logger.warn("[tsf-swagger] swagger or json is null, openApiResource keys:{}, group:{}", openApiResource, groupName);
}
}
catch (Throwable t) {
logger.error("[tsf swagger] init TsfApiMetadataGrapher failed. occur exception: ", t);
}
}
@Override
public void stop() {
isRunning.set(true);
}
@Override
public boolean isRunning() {
return isRunning.get();
}
@Override
public int getPhase() {
return -2;
}
}

@ -0,0 +1,46 @@
/*
* 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.contract.tsf;
import com.tencent.cloud.common.tsf.ConditionalOnTsfConsulEnabled;
import com.tencent.cloud.polaris.contract.config.PolarisContractProperties;
import io.swagger.v3.oas.models.OpenAPI;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.webflux.api.MultipleOpenApiWebFluxResource;
import org.springdoc.webmvc.api.MultipleOpenApiWebMvcResource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
@Configuration
@ConditionalOnTsfConsulEnabled
@ConditionalOnProperty(value = "tsf.swagger.enabled", havingValue = "true", matchIfMissing = true)
public class TsfSwaggerAutoConfiguration {
@Bean
@ConditionalOnBean(OpenAPI.class)
public TsfApiMetadataGrapher tsfApiMetadataGrapher(@Nullable MultipleOpenApiWebMvcResource multipleOpenApiWebMvcResource,
@Nullable MultipleOpenApiWebFluxResource multipleOpenApiWebFluxResource, ApplicationContext context,
PolarisContractProperties polarisContractProperties, ObjectMapperProvider springdocObjectMapperProvider) {
return new TsfApiMetadataGrapher(multipleOpenApiWebMvcResource, multipleOpenApiWebFluxResource,
polarisContractProperties.getGroup(), context, springdocObjectMapperProvider);
}
}

@ -17,26 +17,17 @@
package com.tencent.cloud.polaris.contract.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.tencent.cloud.polaris.contract.SwaggerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.PathSelectors;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.StringUtils;
import static com.google.common.base.Optional.fromNullable;
/**
* Util for package processing.
*
@ -44,87 +35,15 @@ import static com.google.common.base.Optional.fromNullable;
*/
public final class PackageUtil {
/**
* splitter for property.
*/
public static final String SPLITTER = ",";
private static final Logger LOG = LoggerFactory.getLogger(PackageUtil.class);
private static final String SPLITTER = ",";
private PackageUtil() {
}
public static Predicate<RequestHandler> basePackage(String basePackage) {
return input -> declaringClass(input).transform(handlerPackage(basePackage, SPLITTER)).or(false);
}
public static Optional<Class<?>> declaringClass(RequestHandler input) {
if (input == null) {
return Optional.absent();
}
return fromNullable(input.declaringClass());
}
public static Function<Class<?>, Boolean> handlerPackage(String basePackage, String splitter) {
return input -> {
try {
if (StringUtils.isEmpty(basePackage)) {
return false;
}
String[] packages = basePackage.trim().split(splitter);
// Loop to determine matching
for (String strPackage : packages) {
if (input == null) {
continue;
}
Package pkg = input.getPackage();
if (pkg == null) {
continue;
}
String name = pkg.getName();
if (StringUtils.isEmpty(name)) {
continue;
}
boolean isMatch = name.startsWith(strPackage);
if (isMatch) {
return true;
}
}
}
catch (Exception e) {
LOG.error("handler package error", e);
}
return false;
};
}
public static List<Predicate<String>> getExcludePathPredicates(String excludePath) {
List<Predicate<String>> excludePathList = new ArrayList<>();
if (StringUtils.isEmpty(excludePath)) {
return excludePathList;
}
String[] exs = excludePath.split(SPLITTER);
for (String ex : exs) {
if (!StringUtils.isEmpty(ex)) {
excludePathList.add(PathSelectors.ant(ex));
}
}
return excludePathList;
}
public static List<Predicate<String>> getBasePathPredicates(String basePath) {
List<Predicate<String>> basePathList = new ArrayList<>();
if (!StringUtils.isEmpty(basePath)) {
String[] bps = basePath.split(SPLITTER);
for (String bp : bps) {
if (!StringUtils.isEmpty(bp)) {
basePathList.add(PathSelectors.ant(bp));
}
}
}
if (basePathList.isEmpty()) {
basePathList.add(PathSelectors.ant("/**"));
}
return basePathList;
}
public static String scanPackage(String configBasePackage) {
String validScanPackage;
// Externally configured scan package

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

Loading…
Cancel
Save