Compare commits

..
16 Commits
Author SHA1 Message Date
buildmaster d11e0d9dd4 Update SNAPSHOT to 3.1.0-RC1 2021-11-03 11:29:49 +00:00
Olga MaciaszekSharma e5deb642a2 Revert "Temporarily disable codecov."
This reverts commit ba7346573e.
2021-11-02 17:45:59 +01:00
Olga MaciaszekSharma ba7346573e Temporarily disable codecov. 2021-11-02 17:25:27 +01:00
Olga Maciaszek-Sharma 38ccf1be95 Use LB per-client properties. (#622) 2021-11-02 17:24:43 +01:00
spencergibb 5fee522f4c Uses new LoadBalancerClientsProperties class, take 2 2021-10-28 14:48:11 -04:00
spencergibb ce04947d92 Uses new LoadBalancerClientsProperties class 2021-10-28 14:38:26 -04:00
spencergibb 61f71e6ac7 formatting 2021-10-28 14:38:08 -04:00
Olga MaciaszekSharma 939f279a38 Merge remote-tracking branch 'origin/3.0.x' 2021-10-28 18:20:16 +02:00
Hyeonmin Park c0f314a5f0 GitHub workflows: Use Java version in matrix, cache functionality of actions/setup-java@v2 (#614)
* Fix GitHub workflows not using Java version in matrix

* Use cache functionality of actions/setup-java@v2
2021-10-28 18:15:02 +02:00
Sam Kruglov 0cd57b2ce7 cache support (#607) (#608) 2021-10-28 17:59:52 +02:00
spencergibb 1752dd9815 Upgrades openfeign to 11.7 2021-10-26 15:33:04 -04:00
Sam Kruglov b78eacd714 make FeignClientSpecification public (#594) (#609) 2021-10-21 16:47:10 +02:00
buildmaster 56dc4e07e4 Bumping versions to 3.0.6-SNAPSHOT after release 2021-10-21 11:48:48 +00:00
buildmaster 9167752bfb Going back to snapshots 2021-10-21 11:48:47 +00:00
buildmaster 127c866616 Update SNAPSHOT to 3.0.5 2021-10-21 11:46:52 +00:00
buildmaster 876778935c Going back to snapshots 2021-10-21 11:36:13 +00:00
20 changed files with 359 additions and 66 deletions
+5 -10
View File
@@ -20,17 +20,12 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v2
with:
java-version: 1.8
- name: Cache local Maven repository
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
distribution: 'zulu'
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Build with Maven
run: ./mvnw clean install -B -U -P sonar
- uses: codecov/codecov-action@v1
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>3.1.0-M4</version>
<version>3.1.0-RC1</version>
</parent>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<packaging>jar</packaging>
@@ -122,6 +122,7 @@ Spring Cloud OpenFeign provides the following beans by default for feign (`BeanT
* `Encoder` feignEncoder: `SpringEncoder`
* `Logger` feignLogger: `Slf4jLogger`
* `MicrometerCapability` micrometerCapability: If `feign-micrometer` is on the classpath and `MeterRegistry` is available
* `CachingCapability` cachingCapability: If `@EnableCaching` annotation is used. Can be disabled via `feign.cache.enabled`.
* `Contract` feignContract: `SpringMvcContract`
* `Feign.Builder` feignBuilder: `FeignCircuitBreaker.Builder`
* `Client` feignClient: If Spring Cloud LoadBalancer is on the classpath, `FeignBlockingLoadBalancerClient` is used.
@@ -141,7 +142,7 @@ Spring Cloud OpenFeign _does not_ provide the following beans by default for fei
* `Collection<RequestInterceptor>`
* `SetterFactory`
* `QueryMapEncoder`
* `Capability` (`MicrometerCapability` is provided by default)
* `Capability` (`MicrometerCapability` and `CachingCapability` are provided by default)
A bean of `Retryer.NEVER_RETRY` with the type `Retryer` is created by default, which will disable retrying.
Notice this retrying behavior is different from the Feign default one, where it will automatically retry IOExceptions,
@@ -602,6 +603,22 @@ public class FooConfiguration {
}
----
=== Feign Caching
If `@EnableCaching` annotation is used, a `CachingCapability` bean is created and registered so that your Feign client recognizes `@Cache*` annotations on its interface:
[source,java,indent=0]
----
public interface DemoClient {
@GetMapping("/demo/{filterParam}")
@Cacheable(cacheNames = "demo-cache", key = "#keyParam")
String demoEndpoint(String keyParam, @PathVariable String filterParam);
}
----
You can also disable the feature via property `feign.cache.enabled=false`.
=== Feign @QueryMap support
The OpenFeign `@QueryMap` annotation provides support for POJOs to be used as
+3 -3
View File
@@ -4,14 +4,14 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-openfeign</artifactId>
<version>3.1.0-M4</version>
<version>3.1.0-RC1</version>
<packaging>pom</packaging>
<name>Spring Cloud OpenFeign</name>
<description>Spring Cloud OpenFeign</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>3.1.0-M3</version>
<version>3.1.0-RC1</version>
<relativePath/>
</parent>
<scm>
@@ -26,7 +26,7 @@
<properties>
<main.basedir>${basedir}</main.basedir>
<jackson.version>2.11.3</jackson.version>
<spring-cloud-commons.version>3.1.0-M3</spring-cloud-commons.version>
<spring-cloud-commons.version>3.1.0-RC1</spring-cloud-commons.version>
<!-- Plugin versions -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>3.1.0-M4</version>
<version>3.1.0-RC1</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-openfeign-core</artifactId>
@@ -0,0 +1,42 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.openfeign;
import feign.Capability;
import feign.InvocationHandlerFactory;
import org.springframework.cache.interceptor.CacheInterceptor;
/**
* Allows Spring's @Cache* annotations to be declared on the feign client's methods.
*
* @author Sam Kruglov
*/
public class CachingCapability implements Capability {
private final CacheInterceptor cacheInterceptor;
public CachingCapability(CacheInterceptor cacheInterceptor) {
this.cacheInterceptor = cacheInterceptor;
}
@Override
public InvocationHandlerFactory enrich(InvocationHandlerFactory invocationHandlerFactory) {
return new FeignCachingInvocationHandlerFactory(invocationHandlerFactory, cacheInterceptor);
}
}
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import com.fasterxml.jackson.databind.Module;
import feign.Capability;
import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
@@ -50,6 +51,7 @@ 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.cache.interceptor.CacheInterceptor;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
@@ -81,6 +83,7 @@ import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResour
* @author Nguyen Ky Thanh
* @author Andrii Bohutskyi
* @author Kwangyong Kim
* @author Sam Kruglov
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@@ -105,6 +108,13 @@ public class FeignAutoConfiguration {
return context;
}
@Bean
@ConditionalOnProperty(value = "feign.cache.enabled", matchIfMissing = true)
@ConditionalOnBean(CacheInterceptor.class)
public Capability cachingCapability(CacheInterceptor cacheInterceptor) {
return new CachingCapability(cacheInterceptor);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Module.class, Page.class, Sort.class })
@ConditionalOnProperty(value = "feign.autoconfiguration.jackson.enabled", havingValue = "true")
@@ -0,0 +1,82 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.openfeign;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import feign.InvocationHandlerFactory;
import feign.Target;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cache.interceptor.CacheInterceptor;
/**
* Allows Spring's @Cache* annotations to be declared on the feign client's methods.
*
* @author Sam Kruglov
*/
public class FeignCachingInvocationHandlerFactory implements InvocationHandlerFactory {
private final InvocationHandlerFactory delegateFactory;
private final CacheInterceptor cacheInterceptor;
public FeignCachingInvocationHandlerFactory(InvocationHandlerFactory delegateFactory,
CacheInterceptor cacheInterceptor) {
this.delegateFactory = delegateFactory;
this.cacheInterceptor = cacheInterceptor;
}
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
final InvocationHandler delegateHandler = delegateFactory.create(target, dispatch);
return (proxy, method, argsNullable) -> {
Object[] args = Optional.ofNullable(argsNullable).orElseGet(() -> new Object[0]);
return cacheInterceptor.invoke(new MethodInvocation() {
@Override
public Method getMethod() {
return method;
}
@Override
public Object[] getArguments() {
return args;
}
@Override
public Object proceed() throws Throwable {
return delegateHandler.invoke(proxy, method, args);
}
@Override
public Object getThis() {
return target;
}
@Override
public AccessibleObject getStaticPart() {
return method;
}
});
};
}
}
@@ -25,7 +25,7 @@ import org.springframework.cloud.context.named.NamedContextFactory;
* @author Dave Syer
* @author Gregor Zurowski
*/
class FeignClientSpecification implements NamedContextFactory.Specification {
public class FeignClientSpecification implements NamedContextFactory.Specification {
private String name;
@@ -34,7 +34,7 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
FeignClientSpecification() {
}
FeignClientSpecification(String name, Class<?>[] configuration) {
public FeignClientSpecification(String name, Class<?>[] configuration) {
this.name = name;
this.configuration = configuration;
}
@@ -25,7 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@@ -39,15 +39,15 @@ import org.springframework.context.annotation.Configuration;
* @since 2.2.0
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(LoadBalancerProperties.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class DefaultFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties,
public Client feignClient(LoadBalancerClient loadBalancerClient,
LoadBalancerClientFactory loadBalancerClientFactory) {
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, properties,
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient,
loadBalancerClientFactory);
}
@@ -58,10 +58,9 @@ class DefaultFeignLoadBalancerConfiguration {
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) {
return new RetryableFeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient,
loadBalancedRetryFactory, properties, loadBalancerClientFactory);
loadBalancedRetryFactory, loadBalancerClientFactory);
}
}
@@ -60,15 +60,24 @@ public class FeignBlockingLoadBalancerClient implements Client {
private final LoadBalancerClient loadBalancerClient;
private final LoadBalancerProperties properties;
private final LoadBalancerClientFactory loadBalancerClientFactory;
/**
* @deprecated in favour of
* {@link FeignBlockingLoadBalancerClient#FeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancerClientFactory)}
*/
@Deprecated
public FeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
this.delegate = delegate;
this.loadBalancerClient = loadBalancerClient;
this.properties = properties;
this.loadBalancerClientFactory = loadBalancerClientFactory;
}
public FeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
LoadBalancerClientFactory loadBalancerClientFactory) {
this.delegate = delegate;
this.loadBalancerClient = loadBalancerClient;
this.loadBalancerClientFactory = loadBalancerClientFactory;
}
@@ -116,6 +125,7 @@ public class FeignBlockingLoadBalancerClient implements Client {
}
private String getHint(String serviceId) {
LoadBalancerProperties properties = loadBalancerClientFactory.getProperties(serviceId);
String defaultHint = properties.getHint().getOrDefault("default", "default");
String hintPropertyValue = properties.getHint().get(serviceId);
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
@@ -27,7 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration;
import org.springframework.context.annotation.Bean;
@@ -46,16 +46,16 @@ import org.springframework.context.annotation.Import;
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@ConditionalOnProperty(value = "feign.httpclient.hc5.enabled", havingValue = "true")
@Import(HttpClient5FeignConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class HttpClient5FeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient5,
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancerClientFactory loadBalancerClientFactory) {
Client delegate = new ApacheHttp5Client(httpClient5);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, properties, loadBalancerClientFactory);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory);
}
@Bean
@@ -65,11 +65,10 @@ class HttpClient5FeignLoadBalancerConfiguration {
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient5,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) {
Client delegate = new ApacheHttp5Client(httpClient5);
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
properties, loadBalancerClientFactory);
loadBalancerClientFactory);
}
}
@@ -27,7 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.HttpClient5DisabledConditions;
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
@@ -50,16 +50,16 @@ import org.springframework.context.annotation.Import;
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Conditional(HttpClient5DisabledConditions.class)
@Import(HttpClientFeignConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class HttpClientFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancerClientFactory loadBalancerClientFactory) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, properties, loadBalancerClientFactory);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory);
}
@Bean
@@ -69,11 +69,10 @@ class HttpClientFeignLoadBalancerConfiguration {
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
properties, loadBalancerClientFactory);
loadBalancerClientFactory);
}
}
@@ -26,7 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.clientconfig.OkHttpFeignConfiguration;
import org.springframework.context.annotation.Bean;
@@ -46,16 +46,16 @@ import org.springframework.context.annotation.Import;
@ConditionalOnProperty("feign.okhttp.enabled")
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@Import(OkHttpFeignConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class OkHttpFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(okhttp3.OkHttpClient okHttpClient, LoadBalancerClient loadBalancerClient,
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancerClientFactory loadBalancerClientFactory) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, properties, loadBalancerClientFactory);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory);
}
@Bean
@@ -65,11 +65,10 @@ class OkHttpFeignLoadBalancerConfiguration {
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, okhttp3.OkHttpClient okHttpClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
properties, loadBalancerClientFactory);
loadBalancerClientFactory);
}
}
@@ -78,17 +78,27 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
private final LoadBalancerProperties properties;
private final LoadBalancerClientFactory loadBalancerClientFactory;
/**
* @deprecated in favour of
* {@link RetryableFeignBlockingLoadBalancerClient#RetryableFeignBlockingLoadBalancerClient(Client, LoadBalancerClient, LoadBalancedRetryFactory, LoadBalancerClientFactory)}
*/
@Deprecated
public RetryableFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
this.delegate = delegate;
this.loadBalancerClient = loadBalancerClient;
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
this.properties = properties;
this.loadBalancerClientFactory = loadBalancerClientFactory;
}
public RetryableFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory) {
this.delegate = delegate;
this.loadBalancerClient = loadBalancerClient;
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
this.loadBalancerClientFactory = loadBalancerClientFactory;
}
@@ -232,6 +242,7 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
}
private String getHint(String serviceId) {
LoadBalancerProperties properties = loadBalancerClientFactory.getProperties(serviceId);
String defaultHint = properties.getHint().getOrDefault("default", "default");
String hintPropertyValue = properties.getHint().get(serviceId);
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
@@ -0,0 +1,123 @@
/*
* Copyright 2020-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.openfeign;
import java.net.UnknownHostException;
import feign.Contract;
import feign.RequestLine;
import feign.RetryableException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Sam Kruglov
*/
@SpringBootTest(classes = FeignClientCacheTests.TestConfiguration.class)
@DirtiesContext
public class FeignClientCacheTests {
private static final String CACHE_NAME = "foo-cache";
@Autowired
private FooClient foo;
@Test
void cacheExists(@Autowired CacheManager cacheManager) {
assertThat(cacheManager.getCache(CACHE_NAME)).isNotNull();
}
@Test
void interceptedCallsReal() {
assertThatExceptionOfType(RetryableException.class).isThrownBy(foo::getWithCache)
.withRootCauseInstanceOf(UnknownHostException.class);
}
@Test
void nonInterceptedCallsReal() {
assertThatExceptionOfType(RetryableException.class).isThrownBy(foo::getWithoutCache)
.withRootCauseInstanceOf(UnknownHostException.class);
}
@Nested
class givenCached {
String cachedValue = "cached";
@BeforeEach
void setUp(@Autowired CacheManager cacheManager) {
cacheManager.getCache(CACHE_NAME).put(SimpleKey.EMPTY, cachedValue);
}
@Test
void interceptedReturnsCached() {
assertThat(foo.getWithCache()).isSameAs(cachedValue);
}
@Test
void nonInterceptedCallsReal() {
assertThatExceptionOfType(RetryableException.class).isThrownBy(foo::getWithoutCache)
.withRootCauseInstanceOf(UnknownHostException.class);
}
}
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = FooClient.class)
@EnableAutoConfiguration
@EnableCaching
protected static class TestConfiguration {
}
@FeignClient(name = "foo", url = "http://foo", configuration = FooConfiguration.class)
interface FooClient {
@RequestLine("GET /with-cache")
@Cacheable(cacheNames = CACHE_NAME)
String getWithCache();
@RequestLine("GET /without-cache")
String getWithoutCache();
}
public static class FooConfiguration {
@Bean
Contract feignContract() {
return new Contract.Default();
}
}
}
@@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
import feign.Client;
import feign.Request;
import feign.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
@@ -71,16 +72,21 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class FeignBlockingLoadBalancerClientTests {
private Client delegate = mock(Client.class);
private final Client delegate = mock(Client.class);
private BlockingLoadBalancerClient loadBalancerClient = mock(BlockingLoadBalancerClient.class);
private final BlockingLoadBalancerClient loadBalancerClient = mock(BlockingLoadBalancerClient.class);
private final LoadBalancerClientFactory loadBalancerClientFactory = mock(LoadBalancerClientFactory.class);
private final LoadBalancerProperties loadBalancerProperties = new LoadBalancerProperties();
private FeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient = new FeignBlockingLoadBalancerClient(
delegate, loadBalancerClient, loadBalancerProperties, loadBalancerClientFactory);
private final FeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient = new FeignBlockingLoadBalancerClient(
delegate, loadBalancerClient, loadBalancerClientFactory);
@BeforeEach
void setUp() {
when(loadBalancerClientFactory.getProperties(any(String.class))).thenReturn(loadBalancerProperties);
}
@Test
void shouldExtractServiceIdFromRequestUrl() throws IOException {
@@ -76,23 +76,24 @@ import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class RetryableFeignBlockingLoadBalancerClientTests {
private Client delegate = mock(Client.class);
private final Client delegate = mock(Client.class);
private LoadBalancedRetryFactory retryFactory = mock(LoadBalancedRetryFactory.class);
private final LoadBalancedRetryFactory retryFactory = mock(LoadBalancedRetryFactory.class);
private BlockingLoadBalancerClient loadBalancerClient = mock(BlockingLoadBalancerClient.class);
private final BlockingLoadBalancerClient loadBalancerClient = mock(BlockingLoadBalancerClient.class);
private final LoadBalancerClientFactory loadBalancerClientFactory = mock(LoadBalancerClientFactory.class);
private LoadBalancerProperties properties = new LoadBalancerProperties();
private final LoadBalancerProperties properties = new LoadBalancerProperties();
private RetryableFeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient = new RetryableFeignBlockingLoadBalancerClient(
private final RetryableFeignBlockingLoadBalancerClient feignBlockingLoadBalancerClient = new RetryableFeignBlockingLoadBalancerClient(
delegate, loadBalancerClient, retryFactory, properties, loadBalancerClientFactory);
private ServiceInstance serviceInstance = new DefaultServiceInstance("test-a", "test", "testhost", 80, false);
private final ServiceInstance serviceInstance = new DefaultServiceInstance("test-a", "test", "testhost", 80, false);
@BeforeEach
void setUp() {
when(loadBalancerClientFactory.getProperties(any(String.class))).thenReturn(properties);
when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient)))
.thenReturn(new BlockingLoadBalancedRetryPolicy(properties));
when(loadBalancerClient.choose(eq("test"), any())).thenReturn(serviceInstance);
+3 -3
View File
@@ -6,16 +6,16 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.1.0-M3</version>
<version>3.1.0-RC1</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>3.1.0-M4</version>
<version>3.1.0-RC1</version>
<packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description>
<properties>
<feign.version>11.6</feign.version>
<feign.version>11.7</feign.version>
<feign-form.version>3.8.0</feign-form.version>
<spring-security-oauth2-autoconfigure.version>2.1.2.RELEASE</spring-security-oauth2-autoconfigure.version>
</properties>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>3.1.0-M4</version>
<version>3.1.0-RC1</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-starter-openfeign</artifactId>