Compare commits

..
33 Commits
Author SHA1 Message Date
buildmaster a42b4e4b43 Update SNAPSHOT to 3.0.1 2021-01-27 20:50:54 +00:00
Olga Maciaszek-Sharma 793842a9ac Reformat. 2021-01-27 16:54:53 +01:00
Olga Maciaszek-Sharma ec4c935384 Merge remote-tracking branch 'origin/2.2.x' 2021-01-27 16:53:03 +01:00
Olga Maciaszek-Sharma c672d205d7 Use case-insensitive ResponseEntity headers. Fixes gh-456. (#472) 2021-01-27 16:52:29 +01:00
Olga Maciaszek-Sharma 4453424bdb Fix broken reference in docs. 2021-01-27 13:33:57 +01:00
Olga Maciaszek-Sharma db2d07500d Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java
#	spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignPageableEncodingTests.java
2021-01-26 10:03:04 +01:00
Nikita Konev 6b29a1f7df Move Spring Data Jackson Modules from FeignClientsConfiguration to FeignAutoConfiguration - Fix https://github.com/spring-cloud/spring-cloud-openfeign/issues/205 (#430) 2021-01-26 09:23:51 +01:00
Olga Maciaszek-Sharma 252f118bba Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreaker.java
#	spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java
#	src/checkstyle/checkstyle-suppressions.xml
2021-01-22 17:09:30 +01:00
Olga Maciaszek-Sharma bbef08205d Use CircuitBreaker even when no fallback. (#466) 2021-01-22 16:53:32 +01:00
Olga Maciaszek-Sharma 0799f3dd7c Use org security policy.
(cherry picked from commit 25b8202991)
2021-01-22 10:14:04 +01:00
Olga Maciaszek-Sharma 25b8202991 Use org security policy. 2021-01-22 10:11:22 +01:00
Olga Maciaszek-Sharma 5025285c5b Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java
2021-01-20 15:22:52 +01:00
Marcin Grzejszczak 7ef5c0ce96 Lazy openfeign bean registration (#455)
without this change we're eagerly resolving placeholder properties in passed URLs / names. Since this happens at bean definition level it's extremely early e.g. Spring Cloud Contract has not yet registered any placeholders that Feign could try to consume.

with this change we're changing the bean to become lazy initialized and we resolve the placeholder values at runtime - as late as possible.

Fixes gh-441.
# Conflicts:
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java
2021-01-20 15:13:21 +01:00
Olga Maciaszek-Sharma 5f932b0158 Run FeignLoadBalancerAutoConfiguration after BlockingLoadBalancerClientAutoConfiguration and LoadBalancerAutoConfiguration. Fixes gh-454. 2021-01-19 13:12:39 +01:00
Olga Maciaszek-Sharma f5461821a1 Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java
2021-01-19 12:52:43 +01:00
Olga Maciaszek-Sharma 14914b4c97 Fixes gh-460. Remove unused variable. 2021-01-19 12:48:11 +01:00
Olga Maciaszek-Sharma 1b41e39740 Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	docs/src/main/asciidoc/sagan-index.adoc
2021-01-18 11:25:04 +01:00
Olga Maciaszek-Sharma 6bfb1edcf1 Revert "Edit sagan-index."
This reverts commit 01a3898e83.
2021-01-18 11:23:30 +01:00
Olga Maciaszek-Sharma 54a090e77b Edit sagan-index. 2021-01-18 11:21:29 +01:00
Olga Maciaszek-Sharma 8bc9a0eb0a Merge remote-tracking branch 'origin/2.2.x' 2021-01-18 11:20:59 +01:00
Olga Maciaszek-Sharma 01a3898e83 Edit sagan-index. 2021-01-18 11:20:41 +01:00
Olga Maciaszek-Sharma 147ef8c863 Merge remote-tracking branch 'origin/2.2.x' 2021-01-18 11:16:52 +01:00
Olga Maciaszek-Sharma 286360cf7b Add information on contributing, community support and commercial support to project site (#458)
* Add information on contributing, community support and commercial support to project site.

* Fixes after review.
2021-01-18 11:11:32 +01:00
Olga Maciaszek-Sharma 14eb8a7bb1 Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	docs/src/main/asciidoc/_configprops.adoc
2021-01-14 16:02:02 +01:00
Olga Maciaszek-Sharma 5011703eb9 Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	docs/src/main/asciidoc/_configprops.adoc
2021-01-14 15:58:02 +01:00
Olga Maciaszek-Sharma a79d82584e Update docs. 2021-01-14 15:50:47 +01:00
Olga Maciaszek-Sharma c508f68b85 Merge remote-tracking branch 'origin/2.2.x'
# Conflicts:
#	docs/src/main/asciidoc/spring-cloud-openfeign.adoc
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreaker.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerDisabledConditions.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerTargeter.java
#	spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClient.java
#	spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CirciutBreakerTests.java
2021-01-14 15:31:13 +01:00
Olga Maciaszek-Sharma ae3e3efb26 Add license link to README. 2021-01-14 15:21:21 +01:00
Marcin Grzejszczak 01f101a910 Lazy openfeign bean registration (#455)
without this change we're eagerly resolving placeholder properties in passed URLs / names. Since this happens at bean definition level it's extremely early e.g. Spring Cloud Contract has not yet registered any placeholders that Feign could try to consume.

with this change we're changing the bean to become lazy initialized and we resolve the placeholder values at runtime - as late as possible.
2021-01-13 10:15:05 +01:00
buildmaster 69a03ffdb8 Bumping versions 2020-12-22 11:37:23 +00:00
buildmaster 2cf8b55316 Bumping versions to 3.0.1-SNAPSHOT after release 2020-12-21 19:45:02 +00:00
buildmaster eb30c13129 Going back to snapshots 2020-12-21 19:45:02 +00:00
Marcin Grzejszczak e32b10b60f Added support for Spring Cloud CircuitBreaker
fixes gh-279
2020-12-15 18:15:49 +01:00
25 changed files with 472 additions and 164 deletions
+4
View File
@@ -316,3 +316,7 @@ Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on t
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
== License
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/master/LICENSE.txt[here].
-5
View File
@@ -1,5 +0,0 @@
# Security Policy
## Reporting a Vulnerability
To report security vulnerabilities, please go to https://pivotal.io/security.
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
</parent>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<packaging>jar</packaging>
+4
View File
@@ -19,3 +19,7 @@ include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/maste
== Contributing
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[]
== License
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/master/LICENSE.txt[here].
+15
View File
@@ -24,3 +24,18 @@ public class WebApplication {
}
```
## Contributing
We welcome contributions. You can read more on how to contribute to the project https://github.com/spring-cloud/spring-cloud-openfeign/blob/master/README.adoc#3-contributing[here].
## Community Support
* You can report issues through https://github.com/spring-cloud/spring-cloud-openfeign/issues[Github].
* We monitor https://stackoverflow.com/[StackOverflow] for questions with the `spring-cloud-feign` tag.
* You can contact our team at https://gitter.im/spring-cloud/spring-cloud[Gitter].
## Commercial Support
Commercial Support is provided as part of the https://spring.io/support[VMware Spring Runtime] offering.
@@ -15,7 +15,7 @@ To use Feign create an interface and annotate it.
It has pluggable annotation support including Feign annotations and JAX-RS annotations.
Feign also supports pluggable encoders and decoders.
Spring Cloud adds support for Spring MVC annotations and for using the same `HttpMessageConverters` used by default in Spring Web.
Spring Cloud integrates Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
Spring Cloud integrates Eureka, Spring Cloud CircuitBreaker, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
[[netflix-feign-starter]]
=== How to Include Feign
@@ -338,14 +338,14 @@ Spring Cloud CircuitBreaker supports the notion of a fallback: a default code pa
[source,java,indent=0]
----
include::{core_path}/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CirciutBreakerTests.java[tags=client_with_fallback, indent=0]
include::{core_path}/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java[tags=client_with_fallback, indent=0]
----
If one needs access to the cause that made the fallback trigger, one can use the `fallbackFactory` attribute inside `@FeignClient`.
[source,java,indent=0]
----
include::{core_path}/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CirciutBreakerTests.java[tags=client_with_fallback_factory, indent=0]
include::{core_path}/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java[tags=client_with_fallback_factory, indent=0]
----
=== Feign and `@Primary`
@@ -549,7 +549,7 @@ public interface DemoTemplate {
----
=== Feign `CollectionFormat` support
We support `feign.CollectionFormat` by providing the `@CollectionFormat` annotation. You can annotate a Feign client method with it by passing the desired `feign.CollectionFormat` as annotation value.
We support `feign.CollectionFormat` by providing the `@CollectionFormat` annotation.You can annotate a Feign client method with it by passing the desired `feign.CollectionFormat` as annotation value.
In the following example, the `CSV` format is used instead of the default `EXPLODED` to process the method.
@@ -568,7 +568,7 @@ In the following example, the `CSV` format is used instead of the default `EXPLO
TIP: Set the `CSV` format while sending `Pageable` as a query parameter in order for it to be encoded correctly.
=== Reactive Support
As the https://github.com/OpenFeign/feign[OpenFeign project] does not currently support reactive clients, such as https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html[Spring WebClient], neither does Spring Cloud OpenFeign. We will add support for it here as soon as it becomes available in the core project.
As the https://github.com/OpenFeign/feign[OpenFeign project] does not currently support reactive clients, such as https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html[Spring WebClient], neither does Spring Cloud OpenFeign.We will add support for it here as soon as it becomes available in the core project.
Until that is done, we recommend using https://github.com/Playtika/feign-reactive[feign-reactive] for Spring WebClient support.
@@ -583,6 +583,15 @@ To work around this problem you can use an `ObjectProvider` when autowiring your
ObjectProvider<TestFeginClient> testFeginClient;
----
=== Spring Data Support
You may consider enabling Jackson Modules for the support `org.springframework.data.domain.Page` and `org.springframework.data.domain.Sort` decoding.
[source,java]
----
feign.autoconfiguration.jackson.enabled=true
----
== Configuration properties
To see the list of all Sleuth related configuration properties please check link:appendix.html[the Appendix page].
+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.0.0</version>
<version>3.0.1</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.0.0</version>
<version>3.0.1</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.0.0</spring-cloud-commons.version>
<spring-cloud-commons.version>3.0.1</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.0.0</version>
<version>3.0.1</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-openfeign-core</artifactId>
@@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import com.fasterxml.jackson.databind.Module;
import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
@@ -55,6 +56,8 @@ import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor;
import org.springframework.cloud.openfeign.support.DefaultGzipDecoderConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
import org.springframework.cloud.openfeign.support.SortJacksonModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@@ -65,6 +68,8 @@ import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResour
/**
* @author Spencer Gibb
* @author Julien Roy
* @author Grzegorz Poznachowski
* @author Nikita Konev
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@@ -89,6 +94,22 @@ public class FeignAutoConfiguration {
return context;
}
@Bean
@ConditionalOnMissingBean(PageJacksonModule.class)
@ConditionalOnClass(name = "org.springframework.data.domain.Page")
@ConditionalOnProperty(value = "feign.autoconfiguration.jackson.enabled", havingValue = "true")
public Module pageJacksonModule() {
return new PageJacksonModule();
}
@Bean
@ConditionalOnMissingBean(SortJacksonModule.class)
@ConditionalOnClass(name = "org.springframework.data.domain.Sort")
@ConditionalOnProperty(value = "feign.autoconfiguration.jackson.enabled", havingValue = "true")
public Module sortModule() {
return new SortJacksonModule();
}
@Configuration(proxyBeanMethods = false)
@Conditional(FeignCircuitBreakerDisabledConditions.class)
protected static class DefaultFeignTargeterConfiguration {
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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.
@@ -16,12 +16,7 @@
package org.springframework.cloud.openfeign;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Target;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
@@ -73,14 +68,14 @@ public final class FeignCircuitBreaker {
return build(fallbackFactory).newInstance(target);
}
@Override
public <T> T target(Target<T> target) {
return build(null).newInstance(target);
}
public Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new FeignCircuitBreakerInvocationHandler(circuitBreakerFactory, feignClientName, target,
dispatch, nullableFallbackFactory);
}
});
super.invocationHandlerFactory((target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory));
return super.build();
}
@@ -38,6 +38,8 @@ import feign.codec.ErrorDecoder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -57,8 +59,10 @@ import org.springframework.util.StringUtils;
* @author Matt King
* @author Olga Maciaszek-Sharma
* @author Ilia Ilinykh
* @author Marcin Grzejszczak
*/
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
public class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
/***********************************
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
@@ -81,6 +85,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
private Class<?> fallback = void.class;
private Class<?> fallbackFactory = void.class;
@@ -125,7 +131,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
: applicationContext.getBean(FeignClientProperties.class);
FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
@@ -261,7 +268,7 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
private <T> T getOrInstantiate(Class<T> tClass) {
try {
return applicationContext.getBean(tClass);
return beanFactory != null ? beanFactory.getBean(tClass) : applicationContext.getBean(tClass);
}
catch (NoSuchBeanDefinitionException e) {
return BeanUtils.instantiateClass(tClass);
@@ -321,7 +328,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
* information
*/
<T> T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
@@ -436,7 +444,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.applicationContext = context;
applicationContext = context;
beanFactory = context;
}
public Class<?> getFallback() {
@@ -464,7 +473,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
return false;
}
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
return Objects.equals(applicationContext, that.applicationContext) && decode404 == that.decode404
return Objects.equals(applicationContext, that.applicationContext)
&& Objects.equals(beanFactory, that.beanFactory) && decode404 == that.decode404
&& inheritParentContext == that.inheritParentContext && Objects.equals(fallback, that.fallback)
&& Objects.equals(fallbackFactory, that.fallbackFactory) && Objects.equals(name, that.name)
&& Objects.equals(path, that.path) && Objects.equals(type, that.type) && Objects.equals(url, that.url);
@@ -472,8 +482,8 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
@Override
public int hashCode() {
return Objects.hash(applicationContext, decode404, inheritParentContext, fallback, fallbackFactory, name, path,
type, url);
return Objects.hash(applicationContext, beanFactory, decode404, inheritParentContext, fallback, fallbackFactory,
name, path, type, url);
}
@Override
@@ -482,8 +492,14 @@ public class FeignClientFactoryBean implements FactoryBean<Object>, Initializing
.append(name).append("', ").append("url='").append(url).append("', ").append("path='").append(path)
.append("', ").append("decode404=").append(decode404).append(", ").append("inheritParentContext=")
.append(inheritParentContext).append(", ").append("applicationContext=").append(applicationContext)
.append(", ").append("fallback=").append(fallback).append(", ").append("fallbackFactory=")
.append(fallbackFactory).append("}").toString();
.append(", ").append("beanFactory=").append(beanFactory).append(", ").append("fallback=")
.append(fallback).append(", ").append("fallbackFactory=").append(fallbackFactory).append("}")
.toString();
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
@@ -19,7 +19,6 @@ package org.springframework.cloud.openfeign;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.Module;
import feign.Contract;
import feign.Feign;
import feign.Logger;
@@ -44,10 +43,8 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
import org.springframework.cloud.openfeign.support.AbstractFormWriter;
import org.springframework.cloud.openfeign.support.PageJacksonModule;
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SortJacksonModule;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
@@ -142,18 +139,6 @@ public class FeignClientsConfiguration {
return new DefaultFeignLoggerFactory(this.logger);
}
@Bean
@ConditionalOnClass(name = "org.springframework.data.domain.Page")
public Module pageJacksonModule() {
return new PageJacksonModule();
}
@Bean
@ConditionalOnClass(name = "org.springframework.data.domain.Page")
public Module sortModule() {
return new SortJacksonModule();
}
@Bean
@ConditionalOnMissingBean(FeignClientConfigurer.class)
public FeignClientConfigurer feignClientConfigurer() {
@@ -30,6 +30,9 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
@@ -53,6 +56,7 @@ import org.springframework.util.StringUtils;
* @author Venil Noronha
* @author Gang Li
* @author Michal Domagala
* @author Marcin Grzejszczak
*/
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@@ -156,7 +160,6 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
@@ -194,23 +197,40 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
@@ -235,6 +255,10 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
}
/* for testing */ String getName(Map<String, Object> attributes) {
return getName(null, attributes);
}
String getName(ConfigurableBeanFactory beanFactory, Map<String, Object> attributes) {
String name = (String) attributes.get("serviceId");
if (!StringUtils.hasText(name)) {
name = (String) attributes.get("name");
@@ -242,34 +266,42 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
if (!StringUtils.hasText(name)) {
name = (String) attributes.get("value");
}
name = resolve(name);
name = resolve(beanFactory, name);
return getName(name);
}
private String getContextId(Map<String, Object> attributes) {
private String getContextId(ConfigurableBeanFactory beanFactory, Map<String, Object> attributes) {
String contextId = (String) attributes.get("contextId");
if (!StringUtils.hasText(contextId)) {
return getName(attributes);
}
contextId = resolve(contextId);
contextId = resolve(beanFactory, contextId);
return getName(contextId);
}
private String resolve(String value) {
private String resolve(ConfigurableBeanFactory beanFactory, String value) {
if (StringUtils.hasText(value)) {
return this.environment.resolvePlaceholders(value);
if (beanFactory == null) {
return this.environment.resolvePlaceholders(value);
}
BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
String resolved = beanFactory.resolveEmbeddedValue(value);
if (resolver == null) {
return resolved;
}
return String.valueOf(resolver.evaluate(resolved, new BeanExpressionContext(beanFactory, null)));
}
return value;
}
private String getUrl(Map<String, Object> attributes) {
String url = resolve((String) attributes.get("url"));
private String getUrl(ConfigurableBeanFactory beanFactory, Map<String, Object> attributes) {
String url = resolve(beanFactory, (String) attributes.get("url"));
return getUrl(url);
}
private String getPath(Map<String, Object> attributes) {
String path = resolve((String) attributes.get("path"));
private String getPath(ConfigurableBeanFactory beanFactory, Map<String, Object> attributes) {
String path = resolve(beanFactory, (String) attributes.get("path"));
return getPath(path);
}
@@ -19,11 +19,14 @@ package org.springframework.cloud.openfeign.loadbalancer;
import feign.Client;
import feign.Feign;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
@@ -40,6 +43,7 @@ import org.springframework.context.annotation.Import;
@ConditionalOnClass(Feign.class)
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter({ BlockingLoadBalancerClientAutoConfiguration.class, LoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
// Order is important here, last should be the default, first should be optional
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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.
@@ -26,16 +26,16 @@ import feign.Response;
import feign.codec.Decoder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via
* composition.
*
* @author chad jaros
* @author Olga Maciaszek-Sharma
*/
public class ResponseEntityDecoder implements Decoder {
@@ -80,7 +80,7 @@ public class ResponseEntityDecoder implements Decoder {
@SuppressWarnings("unchecked")
private <T> ResponseEntity<T> createResponse(Object instance, Response response) {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
HttpHeaders headers = new HttpHeaders();
for (String key : response.headers().keySet()) {
headers.put(key, new LinkedList<>(response.headers().get(key)));
}
@@ -26,10 +26,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
@@ -49,18 +49,19 @@ import static org.assertj.core.api.Assertions.assertThat;
"feign.client.config.default.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor" })
public class FeignClientUsingConfigurerTest {
private static final String BEAN_NAME_PREFIX = "&org.springframework.cloud.openfeign.FeignClientUsingConfigurerTest$";
private static final String BEAN_NAME_PREFIX = "org.springframework.cloud.openfeign.FeignClientUsingConfigurerTest$";
@Autowired
private ApplicationContext applicationContext;
private ConfigurableListableBeanFactory beanFactory;
@Autowired
private FeignContext context;
@Test
public void testFeignClient() {
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) applicationContext
.getBean(BEAN_NAME_PREFIX + "TestFeignClient");
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) beanFactory
.getBeanDefinition(BEAN_NAME_PREFIX + "TestFeignClient")
.getAttribute("feignClientsRegistrarFactoryBean");
Feign.Builder builder = factoryBean.feign(context);
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder, "requestInterceptors");
@@ -77,8 +78,9 @@ public class FeignClientUsingConfigurerTest {
@Test
public void testNoInheritFeignClient() {
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) applicationContext
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient");
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) beanFactory
.getBeanDefinition(BEAN_NAME_PREFIX + "NoInheritFeignClient")
.getAttribute("feignClientsRegistrarFactoryBean");
Feign.Builder builder = factoryBean.feign(context);
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder, "requestInterceptors");
@@ -89,8 +91,9 @@ public class FeignClientUsingConfigurerTest {
@Test
public void testNoInheritFeignClient_ignoreProperties() {
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) applicationContext
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient");
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) beanFactory
.getBeanDefinition(BEAN_NAME_PREFIX + "NoInheritFeignClient")
.getAttribute("feignClientsRegistrarFactoryBean");
Feign.Builder builder = factoryBean.feign(context);
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set").isEqualTo(Logger.Level.HEADERS);
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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.
@@ -32,7 +32,9 @@ import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -45,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringDecoderTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
@@ -142,6 +145,13 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
assertThat(response.getBody()).as("response body was not null").isNull();
}
@Test
// Issue: https://github.com/spring-cloud/spring-cloud-openfeign/issues/456
public void testResponseEntityHeaders() {
ResponseEntity<String> response = testClient().getContentType();
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
}
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/helloresponse")
@@ -165,6 +175,9 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
@GetMapping("/helloWildcard")
ResponseEntity<?> getWildcard();
@GetMapping(path = "/contentType", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> getContentType();
}
public static class Hello {
@@ -252,6 +265,11 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
return ResponseEntity.ok(new Hello("wildcard"));
}
@Override
public ResponseEntity<String> getContentType() {
return ResponseEntity.ok().header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).body("test");
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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.
@@ -16,10 +16,7 @@
package org.springframework.cloud.openfeign.circuitbreaker;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -59,10 +56,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = CirciutBreakerTests.Application.class, webEnvironment = WebEnvironment.DEFINED_PORT, value = {
@SpringBootTest(classes = CircuitBreakerTests.Application.class, webEnvironment = WebEnvironment.DEFINED_PORT, value = {
"spring.application.name=springcircuittest", "spring.jmx.enabled=false", "feign.circuitbreaker.enabled=true" })
@DirtiesContext
public class CirciutBreakerTests {
public class CircuitBreakerTests {
@Autowired
MyCircuitBreaker myCircuitBreaker;
@@ -185,44 +182,6 @@ public class CirciutBreakerTests {
}
// end::client_with_fallback_factory[]
public static class Hello {
private String message;
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Hello that = (Hello) o;
return Objects.equals(this.message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(this.message);
}
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@@ -280,30 +239,4 @@ public class CirciutBreakerTests {
}
static class MyCircuitBreaker implements CircuitBreaker {
AtomicBoolean runWasCalled = new AtomicBoolean();
@Override
public <T> T run(Supplier<T> toRun) {
this.runWasCalled.set(true);
return toRun.get();
}
@Override
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
try {
return run(toRun);
}
catch (Throwable throwable) {
return fallback.apply(throwable);
}
}
public void clear() {
this.runWasCalled.set(false);
}
}
}
@@ -0,0 +1,155 @@
/*
* 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.circuitbreaker;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
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.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.util.SocketUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for Feign calls with CircuitBreaker, without fallbacks.
*
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest(classes = CircuitBreakerWithNoFallbackTests.Application.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false",
"feign.circuitbreaker.enabled=true" })
@DirtiesContext
public class CircuitBreakerWithNoFallbackTests {
@Autowired
MyCircuitBreaker myCircuitBreaker;
@Autowired
CircuitBreakerTestClient testClient;
@BeforeAll
public static void beforeClass() {
System.setProperty("server.port", String.valueOf(SocketUtils.findAvailableTcpPort()));
}
@AfterAll
public static void afterClass() {
System.clearProperty("server.port");
}
@BeforeEach
public void setup() {
this.myCircuitBreaker.clear();
}
@Test
public void testSimpleTypeWithFallback() {
Hello hello = testClient.getHello();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
assertThat(myCircuitBreaker.runWasCalled).as("Circuit Breaker was called").isTrue();
}
@Test
public void test404WithoutFallback() {
assertThatThrownBy(() -> testClient.getException()).isInstanceOf(NoFallbackAvailableException.class);
}
@FeignClient(name = "test", url = "http://localhost:${server.port}/")
protected interface CircuitBreakerTestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = { CircuitBreakerTestClient.class })
@Import(NoSecurityConfiguration.class)
protected static class Application implements CircuitBreakerTestClient {
static final Log log = LogFactory.getLog(Application.class);
@Bean
MyCircuitBreaker myCircuitBreaker() {
return new MyCircuitBreaker();
}
@Bean
CircuitBreakerFactory circuitBreakerFactory(MyCircuitBreaker myCircuitBreaker) {
return new CircuitBreakerFactory() {
@Override
public CircuitBreaker create(String id) {
log.info("Creating a circuit breaker with id [" + id + "]");
return myCircuitBreaker;
}
@Override
protected ConfigBuilder configBuilder(String id) {
return Object::new;
}
@Override
public void configureDefault(Function defaultConfiguration) {
}
};
}
@Override
public Hello getHello() {
return new Hello("hello world 1");
}
@Override
public String getException() {
throw new IllegalStateException("BOOM!");
}
}
}
@@ -0,0 +1,60 @@
/*
* 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.circuitbreaker;
import java.util.Objects;
/**
* @author Marcin Grzejszczak
*/
class Hello {
private String message;
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Hello that = (Hello) o;
return Objects.equals(this.message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(this.message);
}
}
@@ -0,0 +1,59 @@
/*
* 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.circuitbreaker;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
/**
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
*/
class MyCircuitBreaker implements CircuitBreaker {
AtomicBoolean runWasCalled = new AtomicBoolean();
@Override
public <T> T run(Supplier<T> toRun) {
try {
this.runWasCalled.set(true);
return toRun.get();
}
catch (Throwable throwable) {
throw new NoFallbackAvailableException("No fallback available.", throwable);
}
}
@Override
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
try {
return run(toRun);
}
catch (Throwable throwable) {
return fallback.apply(throwable);
}
}
public void clear() {
this.runWasCalled.set(false);
}
}
@@ -31,7 +31,6 @@ import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
@@ -56,7 +55,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
* @author Charlie Mordant.
*/
@SpringBootTest(classes = FeignPageableEncodingTests.Application.class, webEnvironment = RANDOM_PORT,
value = { "feign.compression.request.enabled=true", "ribbon.OkToRetryOnAllOperations=false" })
value = { "feign.compression.request.enabled=true", "feign.autoconfiguration.jackson.enabled=true" })
@RunWith(SpringJUnit4ClassRunner.class)
public class FeignPageableEncodingTests {
@@ -92,7 +91,7 @@ public class FeignPageableEncodingTests {
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app",
exclude = { RepositoryRestMvcAutoConfiguration.class })
@EnableSpringDataWebSupport
@Import({ NoSecurityConfiguration.class, FeignClientsConfiguration.class })
@Import({ NoSecurityConfiguration.class })
public static class Application {
}
+2 -2
View File
@@ -6,11 +6,11 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>3.0.0</version>
<version>3.0.1</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-starter-openfeign</artifactId>
@@ -11,6 +11,7 @@
<suppress files=".*LoadBalancerFeignClientOverrideTests.*" checks="LineLengthCheck" />
<suppress files=".*ProtobufSpringEncoderTest.*" checks="LineLengthCheck" />
<suppress files=".*ProtobufTest.*" checks="LineLengthCheck" />
<suppress files=".*Hello.*" checks="RedundantModifier"/>
<!-- Important -->
<suppress files=".*HttpEncoding.*" checks="InterfaceIsTypeCheck" />
</suppressions>