diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a405cebe..bf1d7c654 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,3 +25,4 @@
- [feature:remove ConditionalOnConfigReflectEnabled annotation.](https://github.com/Tencent/spring-cloud-tencent/pull/551)
- [Optimize:shutdown server when connect to config server failed.](https://github.com/Tencent/spring-cloud-tencent/pull/552)
- [fix:fix heartbeat interval different configuration from polaris-java SDK.](https://github.com/Tencent/spring-cloud-tencent/pull/560)
+- [Optimize: optimize report call result for restTemplate.](https://github.com/Tencent/spring-cloud-tencent/pull/562)
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
index 0ef6e551c..aab792f3c 100644
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java
@@ -46,6 +46,11 @@ public class MetadataContext {
*/
public static final String FRAGMENT_DISPOSABLE = "disposable";
+ /**
+ * load balancer context.
+ */
+ public static final String FRAGMENT_LOAD_BALANCER = "loadbalancer";
+
/**
* upstream disposable Context.
*/
diff --git a/spring-cloud-tencent-rpc-enhancement/pom.xml b/spring-cloud-tencent-rpc-enhancement/pom.xml
index e311cbff0..72d5113fa 100644
--- a/spring-cloud-tencent-rpc-enhancement/pom.xml
+++ b/spring-cloud-tencent-rpc-enhancement/pom.xml
@@ -28,6 +28,11 @@
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
org.springframework.cloud
spring-cloud-loadbalancer
@@ -71,4 +76,4 @@
-
\ No newline at end of file
+
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java
index 6ba28617d..599b2cc76 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/AbstractPolarisReporterAdapter.java
@@ -53,15 +53,15 @@ public abstract class AbstractPolarisReporterAdapter {
private static final List HTTP_STATUSES = toList(NOT_IMPLEMENTED, BAD_GATEWAY,
SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT, HTTP_VERSION_NOT_SUPPORTED, VARIANT_ALSO_NEGOTIATES,
INSUFFICIENT_STORAGE, LOOP_DETECTED, BANDWIDTH_LIMIT_EXCEEDED, NOT_EXTENDED, NETWORK_AUTHENTICATION_REQUIRED);
- protected final RpcEnhancementReporterProperties properties;
+ protected final RpcEnhancementReporterProperties reportProperties;
/**
* Constructor With {@link RpcEnhancementReporterProperties} .
*
- * @param properties instance of {@link RpcEnhancementReporterProperties}.
+ * @param reportProperties instance of {@link RpcEnhancementReporterProperties}.
*/
- protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties properties) {
- this.properties = properties;
+ protected AbstractPolarisReporterAdapter(RpcEnhancementReporterProperties reportProperties) {
+ this.reportProperties = reportProperties;
}
/**
@@ -88,12 +88,12 @@ public abstract class AbstractPolarisReporterAdapter {
}
else {
// statuses > series
- List status = properties.getStatuses();
+ List status = reportProperties.getStatuses();
if (status.isEmpty()) {
- List series = properties.getSeries();
+ List series = reportProperties.getSeries();
// Check INTERNAL_SERVER_ERROR (500) status.
- if (properties.isIgnoreInternalServerError() && Objects.equals(httpStatus, INTERNAL_SERVER_ERROR)) {
+ if (reportProperties.isIgnoreInternalServerError() && Objects.equals(httpStatus, INTERNAL_SERVER_ERROR)) {
return false;
}
if (series.isEmpty()) {
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java
index 64ecea941..8e22239cb 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java
@@ -27,7 +27,9 @@ import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignPluginRunner;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ExceptionPolarisReporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter;
+import com.tencent.cloud.rpc.enhancement.resttemplate.BlockingLoadBalancerClientAspect;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
+import com.tencent.cloud.rpc.enhancement.resttemplate.RibbonLoadBalancerClientAspect;
import com.tencent.polaris.api.core.ConsumerAPI;
import org.springframework.beans.factory.SmartInitializingSingleton;
@@ -35,6 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
@@ -106,9 +109,8 @@ public class RpcEnhancementAutoConfiguration {
private List restTemplates = Collections.emptyList();
@Bean
- public EnhancedRestTemplateReporter polarisRestTemplateResponseErrorHandler(
- RpcEnhancementReporterProperties properties,
- ConsumerAPI consumerAPI) {
+ public EnhancedRestTemplateReporter enhancedRestTemplateReporter(
+ RpcEnhancementReporterProperties properties, ConsumerAPI consumerAPI) {
return new EnhancedRestTemplateReporter(properties, consumerAPI);
}
@@ -120,5 +122,19 @@ public class RpcEnhancementAutoConfiguration {
}
};
}
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnClass(name = {"org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient"})
+ public RibbonLoadBalancerClientAspect ribbonLoadBalancerClientAspect() {
+ return new RibbonLoadBalancerClientAspect();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnClass(name = {"org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient"})
+ public BlockingLoadBalancerClientAspect blockingLoadBalancerClientAspect() {
+ return new BlockingLoadBalancerClientAspect();
+ }
}
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java
index 11614b6fc..eadaeaaf7 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/plugin/reporter/SuccessPolarisReporter.java
@@ -61,7 +61,7 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple
@Override
public void run(EnhancedFeignContext context) {
- if (!properties.isEnabled()) {
+ if (!reportProperties.isEnabled()) {
return;
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/BlockingLoadBalancerClientAspect.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/BlockingLoadBalancerClientAspect.java
new file mode 100644
index 000000000..bc2cdd05e
--- /dev/null
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/BlockingLoadBalancerClientAspect.java
@@ -0,0 +1,42 @@
+/*
+ * 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.rpc.enhancement.resttemplate;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+/**
+ * Intercept for BlockingLoadBalancerClient, put host&port to thread local.
+ * @author lepdou 2022-09-05
+ */
+@Aspect
+public class BlockingLoadBalancerClientAspect {
+
+ @Pointcut("execution(public * org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient.reconstructURI(..)) ")
+ public void pointcut() {
+
+ }
+
+ @Around("pointcut()")
+ public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
+ LoadBalancerClientAspectUtils.extractLoadBalancerResult(joinPoint);
+ return joinPoint.proceed();
+ }
+}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java
index a617886d5..2a56c39bb 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporter.java
@@ -17,12 +17,12 @@
package com.tencent.cloud.rpc.enhancement.resttemplate;
-import java.net.HttpURLConnection;
+import java.io.IOException;
import java.net.URI;
-import java.net.URL;
+import java.util.Map;
import com.tencent.cloud.common.metadata.MetadataContext;
-import com.tencent.cloud.common.util.ReflectionUtils;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.rpc.enhancement.AbstractPolarisReporterAdapter;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
@@ -33,6 +33,9 @@ import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
@@ -43,13 +46,14 @@ import org.springframework.web.client.ResponseErrorHandler;
*
* @author wh 2022/6/21
*/
-public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler {
+public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter implements ResponseErrorHandler, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class);
- private static final String FIELD_NAME = "connection";
+ static final String HEADER_HAS_ERROR = "X-SCT-Has-Error";
private final ConsumerAPI consumerAPI;
+ private ResponseErrorHandler delegateHandler;
public EnhancedRestTemplateReporter(RpcEnhancementReporterProperties properties, ConsumerAPI consumerAPI) {
super(properties);
@@ -57,30 +61,69 @@ public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter
}
@Override
- public boolean hasError(@NonNull ClientHttpResponse response) {
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ String[] handlerBeanNames = applicationContext.getBeanNamesForType(ResponseErrorHandler.class);
+ if (handlerBeanNames.length == 1) {
+ return;
+ }
+
+ // inject user custom ResponseErrorHandler
+ for (String beanName : handlerBeanNames) {
+ // ignore self
+ if (StringUtils.equalsIgnoreCase("enhancedRestTemplateReporter", beanName)) {
+ continue;
+ }
+ this.delegateHandler = (ResponseErrorHandler) applicationContext.getBean(beanName);
+ }
+ }
+
+ @Override
+ public boolean hasError(@NonNull ClientHttpResponse response) throws IOException {
+ if (delegateHandler != null) {
+ // Preserve the delegated handler result
+ boolean hasError = delegateHandler.hasError(response);
+ response.getHeaders().add(HEADER_HAS_ERROR, String.valueOf(hasError));
+ }
return true;
}
@Override
- public void handleError(@NonNull ClientHttpResponse response) {
+ public void handleError(@NonNull ClientHttpResponse response) throws IOException {
+ if (realHasError(response)) {
+ delegateHandler.handleError(response);
+ }
+
+ clear(response);
}
@Override
- public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response) {
- if (!properties.isEnabled()) {
- return;
+ public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response) throws IOException {
+ // report result to polaris
+ if (reportProperties.isEnabled()) {
+ reportResult(url, response);
}
+ // invoke delegate handler
+ invokeDelegateHandler(url, method, response);
+ }
+
+ private void reportResult(URI url, ClientHttpResponse response) {
ServiceCallResult resultRequest = createServiceCallResult(url);
try {
+ Map loadBalancerContext = MetadataContextHolder.get()
+ .getFragmentContext(MetadataContext.FRAGMENT_LOAD_BALANCER);
- HttpURLConnection connection = (HttpURLConnection) ReflectionUtils.getFieldValue(response, FIELD_NAME);
- if (connection != null) {
- URL realURL = connection.getURL();
- resultRequest.setHost(realURL.getHost());
- resultRequest.setPort(realURL.getPort());
+ String targetHost = loadBalancerContext.get("host");
+ String targetPort = loadBalancerContext.get("port");
+
+ if (StringUtils.isBlank(targetHost) || StringUtils.isBlank(targetPort)) {
+ LOGGER.warn("Can not get target host or port from metadata context. host = {}, port = {}", targetHost, targetPort);
+ return;
}
+ resultRequest.setHost(targetHost);
+ resultRequest.setPort(Integer.parseInt(targetPort));
+
// checking response http status code
if (apply(response.getStatusCode())) {
resultRequest.setRetStatus(RetStatus.RetFail);
@@ -96,6 +139,34 @@ public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter
}
}
+ private void invokeDelegateHandler(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
+ if (realHasError(response)) {
+ delegateHandler.handleError(url, method, response);
+ }
+
+ clear(response);
+ }
+
+ private Boolean realHasError(ClientHttpResponse response) {
+ if (delegateHandler == null) {
+ return false;
+ }
+
+ String hasErrorHeader = response.getHeaders().getFirst(HEADER_HAS_ERROR);
+ if (StringUtils.isBlank(hasErrorHeader)) {
+ return false;
+ }
+
+ return Boolean.parseBoolean(hasErrorHeader);
+ }
+
+ private void clear(ClientHttpResponse response) {
+ if (!response.getHeaders().containsKey(HEADER_HAS_ERROR)) {
+ return;
+ }
+ response.getHeaders().remove(HEADER_HAS_ERROR);
+ }
+
private ServiceCallResult createServiceCallResult(URI uri) {
ServiceCallResult resultRequest = new ServiceCallResult();
String serviceName = uri.getHost();
@@ -110,4 +181,8 @@ public class EnhancedRestTemplateReporter extends AbstractPolarisReporterAdapter
}
return resultRequest;
}
+
+ public void setDelegateHandler(ResponseErrorHandler delegateHandler) {
+ this.delegateHandler = delegateHandler;
+ }
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java
new file mode 100644
index 000000000..0eb2dfd7f
--- /dev/null
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/LoadBalancerClientAspectUtils.java
@@ -0,0 +1,45 @@
+/*
+ * 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.rpc.enhancement.resttemplate;
+
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import org.aspectj.lang.ProceedingJoinPoint;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+
+/**
+ * Extract load balancer result from {@link LoadBalancerClient} and put to MetadataContext.
+ * @author lepdou 2022-09-06
+ */
+public final class LoadBalancerClientAspectUtils {
+
+ private LoadBalancerClientAspectUtils() {
+ }
+
+ public static void extractLoadBalancerResult(ProceedingJoinPoint joinPoint) {
+ Object server = joinPoint.getArgs()[0];
+ if (server instanceof ServiceInstance) {
+ ServiceInstance instance = (ServiceInstance) server;
+ MetadataContextHolder.get().putContext(MetadataContext.FRAGMENT_LOAD_BALANCER, "host", instance.getHost());
+ MetadataContextHolder.get()
+ .putContext(MetadataContext.FRAGMENT_LOAD_BALANCER, "port", String.valueOf(instance.getPort()));
+ }
+ }
+}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/RibbonLoadBalancerClientAspect.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/RibbonLoadBalancerClientAspect.java
new file mode 100644
index 000000000..12b469735
--- /dev/null
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/RibbonLoadBalancerClientAspect.java
@@ -0,0 +1,42 @@
+/*
+ * 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.rpc.enhancement.resttemplate;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+/**
+ * Intercept for RibbonLoadBalancerClient, put host&port to thread local.
+ * @author lepdou 2022-09-05
+ */
+@Aspect
+public class RibbonLoadBalancerClientAspect {
+
+ @Pointcut("execution(public * org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.reconstructURI(..))")
+ public void pointcut() {
+
+ }
+
+ @Around("pointcut()")
+ public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
+ LoadBalancerClientAspectUtils.extractLoadBalancerResult(joinPoint);
+ return joinPoint.proceed();
+ }
+}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java
index 848e22980..173cb727d 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateReporterTest.java
@@ -17,55 +17,180 @@
package com.tencent.cloud.rpc.enhancement.resttemplate;
-
-import java.net.HttpURLConnection;
+import java.io.IOException;
+import java.io.InputStream;
import java.net.URI;
-import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
-import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.AbstractClientHttpResponse;
+import org.springframework.web.client.ResponseErrorHandler;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
- * Test For {@link EnhancedRestTemplateReporter}.
- *
- * @author wh 2022/6/22
+ * Test for {@link EnhancedRestTemplateReporter}
+ * @author lepdou 2022-09-06
*/
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = EnhancedRestTemplateReporterTest.TestApplication.class,
- properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"})
+@RunWith(MockitoJUnitRunner.class)
public class EnhancedRestTemplateReporterTest {
+ private static MockedStatic mockedMetadataContextHolder;
+ private static MockedStatic mockedApplicationContextAwareUtils;
+ @Mock
+ private ConsumerAPI consumerAPI;
+ @Mock
+ private RpcEnhancementReporterProperties reporterProperties;
+ @Mock
+ private ResponseErrorHandler delegate;
+ @InjectMocks
+ private EnhancedRestTemplateReporter enhancedRestTemplateReporter;
+
+ @BeforeClass
+ public static void beforeClass() {
+ mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
+ mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
+ .thenReturn("caller");
+ MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
+
+ // mock transitive metadata
+ Map loadBalancerContext = new HashMap<>();
+ loadBalancerContext.put("host", "1.1.1.1");
+ loadBalancerContext.put("port", "8080");
+ when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_LOAD_BALANCER)).thenReturn(loadBalancerContext);
+
+ mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
+ mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ mockedApplicationContextAwareUtils.close();
+ mockedMetadataContextHolder.close();
+ }
+
+ @Before
+ public void before() {
+ enhancedRestTemplateReporter.setDelegateHandler(delegate);
+ }
+
@Test
- public void handleError() throws Exception {
- ConsumerAPI consumerAPI = mock(ConsumerAPI.class);
- EnhancedRestTemplateReporter enhancedRestTemplateReporter =
- new EnhancedRestTemplateReporter(mock(RpcEnhancementReporterProperties.class), consumerAPI);
+ public void testHasError() throws IOException {
+ when(delegate.hasError(any())).thenReturn(true);
+
+ MockedClientHttpResponse response = new MockedClientHttpResponse();
+ Assert.assertTrue(enhancedRestTemplateReporter.hasError(response));
+
+ String realHasError = response.getHeaders().getFirst(EnhancedRestTemplateReporter.HEADER_HAS_ERROR);
+ Assert.assertEquals("true", realHasError);
+ }
+
+ @Test
+ public void testHandleHasError() throws IOException {
+ when(reporterProperties.isEnabled()).thenReturn(true);
+ when(delegate.hasError(any())).thenReturn(true);
+
+ MockedClientHttpResponse response = new MockedClientHttpResponse();
+ enhancedRestTemplateReporter.hasError(response);
+
URI uri = mock(URI.class);
- when(uri.getPath()).thenReturn("/test");
- when(uri.getHost()).thenReturn("host");
- HttpURLConnection httpURLConnection = mock(HttpURLConnection.class);
- URL url = mock(URL.class);
- when(httpURLConnection.getURL()).thenReturn(url);
- when(url.getHost()).thenReturn("127.0.0.1");
- when(url.getPort()).thenReturn(8080);
- when(httpURLConnection.getResponseCode()).thenReturn(200);
- SimpleClientHttpResponseTest clientHttpResponse = new SimpleClientHttpResponseTest(httpURLConnection);
- enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, clientHttpResponse);
- when(consumerAPI.unWatchService(null)).thenReturn(true);
+ enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, response);
+
+ verify(consumerAPI).updateServiceCallResult(any());
+ verify(delegate).handleError(uri, HttpMethod.GET, response);
}
- @SpringBootApplication
- protected static class TestApplication {
+ @Test
+ public void testHandleHasNotError() throws IOException {
+ when(reporterProperties.isEnabled()).thenReturn(true);
+ when(delegate.hasError(any())).thenReturn(false);
+
+ MockedClientHttpResponse response = new MockedClientHttpResponse();
+ enhancedRestTemplateReporter.hasError(response);
+
+ URI uri = mock(URI.class);
+ enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, response);
+
+ verify(consumerAPI).updateServiceCallResult(any());
+ verify(delegate, times(0)).handleError(uri, HttpMethod.GET, response);
+ }
+
+ @Test
+ public void testReportSwitchOff() throws IOException {
+ when(reporterProperties.isEnabled()).thenReturn(false);
+ when(delegate.hasError(any())).thenReturn(true);
+
+ MockedClientHttpResponse response = new MockedClientHttpResponse();
+ enhancedRestTemplateReporter.hasError(response);
+
+ URI uri = mock(URI.class);
+ enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, response);
+
+ verify(consumerAPI, times(0)).updateServiceCallResult(any());
+ verify(delegate).handleError(uri, HttpMethod.GET, response);
+ }
+
+ class MockedClientHttpResponse extends AbstractClientHttpResponse {
+
+ private HttpHeaders headers;
+
+ MockedClientHttpResponse() {
+ this.headers = new HttpHeaders();
+ }
+
+ @Override
+ public int getRawStatusCode() throws IOException {
+ return 0;
+ }
+
+ @Override
+ public String getStatusText() throws IOException {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public InputStream getBody() throws IOException {
+ return null;
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ return headers;
+ }
+ @Override
+ public HttpStatus getStatusCode() throws IOException {
+ return HttpStatus.OK;
+ }
}
}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/SimpleClientHttpResponseTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/SimpleClientHttpResponseTest.java
deleted file mode 100644
index 0899da7a1..000000000
--- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/resttemplate/SimpleClientHttpResponseTest.java
+++ /dev/null
@@ -1,105 +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.rpc.enhancement.resttemplate;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.client.AbstractClientHttpResponse;
-import org.springframework.lang.Nullable;
-import org.springframework.util.StreamUtils;
-import org.springframework.util.StringUtils;
-
-
-/**
- * Mock Test for {@link AbstractClientHttpResponse}.
- *
- * @author wh 2022/6/22
- */
-public class SimpleClientHttpResponseTest extends AbstractClientHttpResponse {
-
- private final HttpURLConnection connection;
-
- @Nullable
- private HttpHeaders headers;
-
- @Nullable
- private InputStream responseStream;
-
-
- SimpleClientHttpResponseTest(HttpURLConnection connection) {
- this.connection = connection;
- }
-
-
- @Override
- public int getRawStatusCode() throws IOException {
- return this.connection.getResponseCode();
- }
-
- @Override
- public String getStatusText() throws IOException {
- String result = this.connection.getResponseMessage();
- return (result != null) ? result : "";
- }
-
- @Override
- public HttpHeaders getHeaders() {
- if (this.headers == null) {
- this.headers = new HttpHeaders();
- // Header field 0 is the status line for most HttpURLConnections, but not on GAE
- String name = this.connection.getHeaderFieldKey(0);
- if (StringUtils.hasLength(name)) {
- this.headers.add(name, this.connection.getHeaderField(0));
- }
- int i = 1;
- while (true) {
- name = this.connection.getHeaderFieldKey(i);
- if (!StringUtils.hasLength(name)) {
- break;
- }
- this.headers.add(name, this.connection.getHeaderField(i));
- i++;
- }
- }
- return this.headers;
- }
-
- @Override
- public InputStream getBody() throws IOException {
- InputStream errorStream = this.connection.getErrorStream();
- this.responseStream = (errorStream != null ? errorStream : this.connection.getInputStream());
- return this.responseStream;
- }
-
- @Override
- public void close() {
- try {
- if (this.responseStream == null) {
- getBody();
- }
- StreamUtils.drain(this.responseStream);
- this.responseStream.close();
- }
- catch (Exception ex) {
- // ignore
- }
- }
-}