Added checkstyle

This commit is contained in:
Marcin Grzejszczak
2019-02-05 18:46:31 +01:00
parent 1f37891ffe
commit c8c7452848
120 changed files with 6686 additions and 4301 deletions

14
.editorconfig Normal file
View File

@@ -0,0 +1,14 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = tab
indent_size = 4
end_of_line = lf
insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2

View File

@@ -1,66 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<servers>
<server>
<id>repo.spring.io</id>
<username>${env.CI_DEPLOY_USERNAME}</username>
<password>${env.CI_DEPLOY_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<!--
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
a native Maven with the right version. Eclipse users should points their Maven tooling to
this settings file, or copy the profile into their ~/.m2/settings.xml.
-->
<id>spring</id>
<activation><activeByDefault>true</activeByDefault></activation>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<servers>
<server>
<id>repo.spring.io</id>
<username>${env.CI_DEPLOY_USERNAME}</username>
<password>${env.CI_DEPLOY_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<!--
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
a native Maven with the right version. Eclipse users should points their Maven tooling to
this settings file, or copy the profile into their ~/.m2/settings.xml.
-->
<id>spring</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</settings>

0
.springformat Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>

55
pom.xml
View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-openfeign</artifactId>
<version>2.1.1.BUILD-SNAPSHOT</version>
@@ -11,12 +12,15 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.1.3.BUILD-SNAPSHOT</version>
<relativePath />
<relativePath/>
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git</developerConnection>
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git
</connection>
<developerConnection>
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git
</developerConnection>
<tag>HEAD</tag>
</scm>
<properties>
@@ -32,8 +36,15 @@
<surefire.plugin.version>2.19.1</surefire.plugin.version>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec
</sonar.jacoco.reportPath>
<sonar.language>java</sonar.language>
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
<maven-checkstyle-plugin.failsOnViolation>true
</maven-checkstyle-plugin.failsOnViolation>
<maven-checkstyle-plugin.includeTestSourceDirectory>true
</maven-checkstyle-plugin.includeTestSourceDirectory>
</properties>
<build>
<plugins>
@@ -46,11 +57,13 @@
<additionalConfig>
<file>
<name>.settings/org.eclipse.jdt.ui.prefs</name>
<location>${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs</location>
<location>${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs
</location>
</file>
<file>
<name>.settings/org.eclipse.jdt.core.prefs</name>
<location>${main.basedir}/eclipse/org.eclipse.jdt.core.prefs</location>
<location>${main.basedir}/eclipse/org.eclipse.jdt.core.prefs
</location>
</file>
</additionalConfig>
</configuration>
@@ -63,8 +76,26 @@
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<dependencyManagement>
<dependencies>
<dependency>
@@ -106,7 +137,7 @@
<module>spring-cloud-openfeign-core</module>
<module>spring-cloud-starter-openfeign</module>
<module>docs</module>
</modules>
</modules>
<profiles>
<profile>
<id>spring</id>
@@ -184,7 +215,8 @@
</goals>
<configuration>
<propertyName>surefireArgLine</propertyName>
<destFile>${project.build.directory}/jacoco.exec</destFile>
<destFile>${project.build.directory}/jacoco.exec
</destFile>
</configuration>
</execution>
<execution>
@@ -195,7 +227,8 @@
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
<dataFile>${project.build.directory}/jacoco.exec
</dataFile>
</configuration>
</execution>
</executions>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -218,18 +219,18 @@
<profiles>
<profile>
<id>java8plus</id>
<activation>
<jdk>[1.8,2.0)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<activation>
<jdk>[1.8,2.0)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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,20 +32,19 @@ public interface AnnotatedParameterProcessor {
/**
* Retrieves the processor supported annotation type.
*
* @return the annotation type
*/
Class<? extends Annotation> getAnnotationType();
/**
* Process the annotated parameter.
*
* @param context the parameter context
* @param context the parameter context
* @param annotation the annotation instance
* @param method the method that contains the annotation
* @param method the method that contains the annotation
* @return whether the parameter is http
*/
boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method);
boolean processArgument(AnnotatedParameterContext context, Annotation annotation,
Method method);
/**
* Specifies the parameter context.
@@ -56,32 +55,30 @@ public interface AnnotatedParameterProcessor {
/**
* Retrieves the method metadata.
*
* @return the method metadata
*/
MethodMetadata getMethodMetadata();
/**
* Retrieves the index of the parameter.
*
* @return the parameter index
*/
int getParameterIndex();
/**
* Sets the parameter name.
*
* @param name the name of the parameter
*/
void setParameterName(String name);
/**
* Sets the template parameter.
*
* @param name the template parameter
* @param rest the existing parameter values
* @return parameters
*/
Collection<String> setTemplateParameter(String name, Collection<String> rest);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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;
@@ -26,8 +25,9 @@ import feign.Target;
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -55,7 +55,6 @@ public @interface EnableFeignClients {
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the array of 'basePackages'.
*/
String[] basePackages() default {};
@@ -66,7 +65,6 @@ public @interface EnableFeignClients {
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
*
* @return the array of 'basePackageClasses'.
*/
Class<?>[] basePackageClasses() default {};
@@ -77,12 +75,15 @@ public @interface EnableFeignClients {
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
* @return list of default configurations
*/
Class<?>[] defaultConfiguration() default {};
/**
* List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
* List of classes annotated with @FeignClient. If not empty, disables classpath
* scanning.
* @return list of FeignClient classes
*/
Class<?>[] clients() default {};
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -22,11 +22,19 @@ import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import feign.Client;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -42,21 +50,14 @@ import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.Client;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import javax.annotation.PreDestroy;
/**
* @author Spencer Gibb
* @author Julien Roy
*/
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
public class FeignAutoConfiguration {
@Autowired(required = false)
@@ -77,21 +78,25 @@ public class FeignAutoConfiguration {
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
// the following configuration is for alternate feign clients if
@@ -104,6 +109,7 @@ public class FeignAutoConfiguration {
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@@ -118,10 +124,12 @@ public class FeignAutoConfiguration {
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
httpClientProperties.getTimeToLiveUnit(),
this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
@@ -139,9 +147,9 @@ public class FeignAutoConfiguration {
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder().
setConnectionManager(httpClientConnectionManager).
setDefaultRequestConfig(defaultRequestConfig).build();
this.httpClient = httpClientFactory.createBuilder()
.setConnectionManager(httpClientConnectionManager)
.setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
@@ -153,26 +161,28 @@ public class FeignAutoConfiguration {
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
public ConnectionPool httpClientConnectionPool(
FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
@@ -181,22 +191,23 @@ public class FeignAutoConfiguration {
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
ConnectionPool connectionPool,
FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects).connectionPool(connectionPool)
.build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
if (this.okHttpClient != null) {
this.okHttpClient.dispatcher().executorService().shutdown();
this.okHttpClient.connectionPool().evictAll();
}
}
@@ -205,6 +216,7 @@ public class FeignAutoConfiguration {
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -42,41 +42,45 @@ public @interface FeignClient {
* The name of the service with optional protocol prefix. Synonym for {@link #name()
* name}. A name must be specified for all clients, whether or not a url is provided.
* Can be specified as property key, eg: ${propertyKey}.
* @return the name of the service with optional protocol prefix
*/
@AliasFor("name")
String value() default "";
/**
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
*
* @deprecated use {@link #name() name} instead
* @return the service id with optional protocol prefix
*/
@Deprecated
String serviceId() default "";
/**
* This will be used as the bean name instead of name if present, but will not be used as a service id.
* This will be used as the bean name instead of name if present, but will not be used
* as a service id.
* @return bean name instead of name if present
*/
String contextId() default "";
/**
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
* @return The service id with optional protocol prefix. Synonym for {@link #value()
* value}.
*/
@AliasFor("value")
String name() default "";
/**
* Sets the <code>@Qualifier</code> value for the feign client.
* @return the <code>@Qualifier</code> value for the feign client.
*/
String qualifier() default "";
/**
* An absolute URL or resolvable hostname (the protocol is optional).
* @return an absolute URL or resolvable hostname (the protocol is optional).
*/
String url() default "";
/**
* Whether 404s should be decoded instead of throwing FeignExceptions
* @return whether 404s should be decoded instead of throwing FeignExceptions
*/
boolean decode404() default false;
@@ -86,33 +90,35 @@ public @interface FeignClient {
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
* @return list of configurations for feign client
*/
Class<?>[] configuration() default {};
/**
* Fallback class for the specified Feign client interface. The fallback class must
* implement the interface annotated by this annotation and be a valid spring bean.
* @return fallback class for the specified Feign client interface
*/
Class<?> fallback() default void.class;
/**
* Define a fallback factory for the specified Feign client interface. The fallback
* factory must produce instances of fallback classes that implement the interface
* annotated by {@link FeignClient}. The fallback factory must be a valid spring
* bean.
* annotated by {@link FeignClient}. The fallback factory must be a valid spring bean.
*
* @see feign.hystrix.FallbackFactory for details.
* @return fallback factory for the specified Feign client interface
*/
Class<?> fallbackFactory() default void.class;
/**
* Path prefix to be used by all method-level mappings. Can be used with or without
* <code>@RibbonClient</code>.
* @return path prefix to be used by all method-level mappings. Can be used with or
* without <code>@RibbonClient</code>.
*/
String path() default "";
/**
* Whether to mark the feign proxy as a primary bean. Defaults to true.
* @return whether to mark the feign proxy as a primary bean. Defaults to true.
*/
boolean primary() default true;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -35,10 +35,15 @@ public class FeignClientBuilder {
}
public <T> Builder<T> forType(final Class<T> type, final String name) {
return new Builder<>(applicationContext, type, name);
return new Builder<>(this.applicationContext, type, name);
}
public static class Builder<T> {
/**
* Builder of feign targets.
*
* @param <T> type of target
*/
public static final class Builder<T> {
private FeignClientFactoryBean feignClientFactoryBean;
@@ -95,5 +100,7 @@ public class FeignClientBuilder {
public <T> T build() {
return this.feignClientFactoryBean.getTarget();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -19,17 +19,6 @@ package org.springframework.cloud.openfeign;
import java.util.Map;
import java.util.Objects;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import feign.Client;
import feign.Contract;
import feign.Feign;
@@ -42,16 +31,29 @@ import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Spencer Gibb
* @author Venil Noronha
* @author Eko Kurniawan Khannedy
* @author Gregor Zurowski
*/
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
ApplicationContextAware {
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
/***********************************
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition.
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
* lifecycle race condition.
***********************************/
private Class<?> type;
@@ -78,11 +80,6 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
Assert.hasText(this.name, "Name must be set");
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.applicationContext = context;
}
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
@@ -102,23 +99,33 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
FeignClientProperties properties = this.applicationContext
.getBean(FeignClientProperties.class);
if (properties != null) {
if (properties.isDefaultToProperties()) {
configureUsingConfiguration(context, builder);
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.contextId), builder);
} else {
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(this.contextId), builder);
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
}
else {
configureUsingProperties(
properties.getConfig().get(properties.getDefaultConfig()),
builder);
configureUsingProperties(properties.getConfig().get(this.contextId),
builder);
configureUsingConfiguration(context, builder);
}
} else {
}
else {
configureUsingConfiguration(context, builder);
}
}
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
protected void configureUsingConfiguration(FeignContext context,
Feign.Builder builder) {
Logger.Level level = getOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
@@ -135,18 +142,20 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
if (options != null) {
builder.options(options);
}
Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
this.contextId, RequestInterceptor.class);
Map<String, RequestInterceptor> requestInterceptors = context
.getInstances(this.contextId, RequestInterceptor.class);
if (requestInterceptors != null) {
builder.requestInterceptors(requestInterceptors.values());
}
if (decode404) {
if (this.decode404) {
builder.decode404();
}
}
protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
protected void configureUsingProperties(
FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
if (config == null) {
return;
}
@@ -156,7 +165,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
builder.options(new Request.Options(config.getConnectTimeout(),
config.getReadTimeout()));
}
if (config.getRetryer() != null) {
@@ -169,7 +179,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
builder.errorDecoder(errorDecoder);
}
if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
if (config.getRequestInterceptors() != null
&& !config.getRequestInterceptors().isEmpty()) {
// this will add request interceptor to builder, not replace existing
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
RequestInterceptor interceptor = getOrInstantiate(bean);
@@ -198,8 +209,9 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
private <T> T getOrInstantiate(Class<T> tClass) {
try {
return applicationContext.getBean(tClass);
} catch (NoSuchBeanDefinitionException e) {
return this.applicationContext.getBean(tClass);
}
catch (NoSuchBeanDefinitionException e) {
return BeanUtils.instantiateClass(tClass);
}
}
@@ -207,8 +219,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
protected <T> T get(FeignContext context, Class<T> type) {
T instance = context.getInstance(this.contextId, type);
if (instance == null) {
throw new IllegalStateException("No bean found of type " + type + " for "
+ this.contextId);
throw new IllegalStateException(
"No bean found of type " + type + " for " + this.contextId);
}
return instance;
}
@@ -237,22 +249,23 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
/**
* @param <T> the target type of the Feign client
* @return a {@link Feign} client created with the specified data and the context information
* @return a {@link Feign} client created with the specified data and the context
* information
*/
<T> T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
FeignContext context = this.applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
this.url = "http://" + this.name;
}
else {
url = this.name;
this.url = this.name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
@@ -263,13 +276,13 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
private String cleanPath() {
@@ -296,7 +309,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public Class<?> getType() {
return type;
return this.type;
}
public void setType(Class<?> type) {
@@ -304,7 +317,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public String getName() {
return name;
return this.name;
}
public void setName(String name) {
@@ -312,7 +325,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public String getContextId() {
return contextId;
return this.contextId;
}
public void setContextId(String contextId) {
@@ -320,7 +333,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public String getUrl() {
return url;
return this.url;
}
public void setUrl(String url) {
@@ -328,7 +341,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public String getPath() {
return path;
return this.path;
}
public void setPath(String path) {
@@ -336,7 +349,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public boolean isDecode404() {
return decode404;
return this.decode404;
}
public void setDecode404(boolean decode404) {
@@ -344,11 +357,16 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public ApplicationContext getApplicationContext() {
return applicationContext;
return this.applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.applicationContext = context;
}
public Class<?> getFallback() {
return fallback;
return this.fallback;
}
public void setFallback(Class<?> fallback) {
@@ -356,7 +374,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
}
public Class<?> getFallbackFactory() {
return fallbackFactory;
return this.fallbackFactory;
}
public void setFallbackFactory(Class<?> fallbackFactory) {
@@ -365,37 +383,39 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
return Objects.equals(applicationContext, that.applicationContext) &&
decode404 == that.decode404 &&
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);
return Objects.equals(this.applicationContext, that.applicationContext)
&& this.decode404 == that.decode404
&& Objects.equals(this.fallback, that.fallback)
&& Objects.equals(this.fallbackFactory, that.fallbackFactory)
&& Objects.equals(this.name, that.name)
&& Objects.equals(this.path, that.path)
&& Objects.equals(this.type, that.type)
&& Objects.equals(this.url, that.url);
}
@Override
public int hashCode() {
return Objects.hash(applicationContext, decode404, fallback, fallbackFactory,
name, path, type, url);
return Objects.hash(this.applicationContext, this.decode404, this.fallback,
this.fallbackFactory, this.name, this.path, this.type, this.url);
}
@Override
public String toString() {
return new StringBuilder("FeignClientFactoryBean{")
.append("type=").append(type).append(", ")
.append("name='").append(name).append("', ")
.append("url='").append(url).append("', ")
.append("path='").append(path).append("', ")
.append("decode404=").append(decode404).append(", ")
.append("applicationContext=").append(applicationContext).append(", ")
.append("fallback=").append(fallback).append(", ")
.append("fallbackFactory=").append(fallbackFactory)
.append("}").toString();
return new StringBuilder("FeignClientFactoryBean{").append("type=")
.append(this.type).append(", ").append("name='").append(this.name)
.append("', ").append("url='").append(this.url).append("', ")
.append("path='").append(this.path).append("', ").append("decode404=")
.append(this.decode404).append(", ").append("applicationContext=")
.append(this.applicationContext).append(", ").append("fallback=")
.append(this.fallback).append(", ").append("fallbackFactory=")
.append(this.fallbackFactory).append("}").toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -13,8 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import feign.Contract;
import feign.Logger;
import feign.RequestInterceptor;
@@ -22,12 +28,8 @@ import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Eko Kurniawan Khannedy
@@ -42,7 +44,7 @@ public class FeignClientProperties {
private Map<String, FeignClientConfiguration> config = new HashMap<>();
public boolean isDefaultToProperties() {
return defaultToProperties;
return this.defaultToProperties;
}
public void setDefaultToProperties(boolean defaultToProperties) {
@@ -50,7 +52,7 @@ public class FeignClientProperties {
}
public String getDefaultConfig() {
return defaultConfig;
return this.defaultConfig;
}
public void setDefaultConfig(String defaultConfig) {
@@ -58,7 +60,7 @@ public class FeignClientProperties {
}
public Map<String, FeignClientConfiguration> getConfig() {
return config;
return this.config;
}
public void setConfig(Map<String, FeignClientConfiguration> config) {
@@ -67,19 +69,26 @@ public class FeignClientProperties {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FeignClientProperties that = (FeignClientProperties) o;
return defaultToProperties == that.defaultToProperties &&
Objects.equals(defaultConfig, that.defaultConfig) &&
Objects.equals(config, that.config);
return this.defaultToProperties == that.defaultToProperties
&& Objects.equals(this.defaultConfig, that.defaultConfig)
&& Objects.equals(this.config, that.config);
}
@Override
public int hashCode() {
return Objects.hash(defaultToProperties, defaultConfig, config);
return Objects.hash(this.defaultToProperties, this.defaultConfig, this.config);
}
/**
* Feign client configuration.
*/
public static class FeignClientConfiguration {
private Logger.Level loggerLevel;
@@ -103,7 +112,7 @@ public class FeignClientProperties {
private Class<Contract> contract;
public Logger.Level getLoggerLevel() {
return loggerLevel;
return this.loggerLevel;
}
public void setLoggerLevel(Logger.Level loggerLevel) {
@@ -111,7 +120,7 @@ public class FeignClientProperties {
}
public Integer getConnectTimeout() {
return connectTimeout;
return this.connectTimeout;
}
public void setConnectTimeout(Integer connectTimeout) {
@@ -119,7 +128,7 @@ public class FeignClientProperties {
}
public Integer getReadTimeout() {
return readTimeout;
return this.readTimeout;
}
public void setReadTimeout(Integer readTimeout) {
@@ -127,7 +136,7 @@ public class FeignClientProperties {
}
public Class<Retryer> getRetryer() {
return retryer;
return this.retryer;
}
public void setRetryer(Class<Retryer> retryer) {
@@ -135,7 +144,7 @@ public class FeignClientProperties {
}
public Class<ErrorDecoder> getErrorDecoder() {
return errorDecoder;
return this.errorDecoder;
}
public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {
@@ -143,15 +152,16 @@ public class FeignClientProperties {
}
public List<Class<RequestInterceptor>> getRequestInterceptors() {
return requestInterceptors;
return this.requestInterceptors;
}
public void setRequestInterceptors(List<Class<RequestInterceptor>> requestInterceptors) {
public void setRequestInterceptors(
List<Class<RequestInterceptor>> requestInterceptors) {
this.requestInterceptors = requestInterceptors;
}
public Boolean getDecode404() {
return decode404;
return this.decode404;
}
public void setDecode404(Boolean decode404) {
@@ -159,7 +169,7 @@ public class FeignClientProperties {
}
public Class<Decoder> getDecoder() {
return decoder;
return this.decoder;
}
public void setDecoder(Class<Decoder> decoder) {
@@ -167,7 +177,7 @@ public class FeignClientProperties {
}
public Class<Encoder> getEncoder() {
return encoder;
return this.encoder;
}
public void setEncoder(Class<Encoder> encoder) {
@@ -175,7 +185,7 @@ public class FeignClientProperties {
}
public Class<Contract> getContract() {
return contract;
return this.contract;
}
public void setContract(Class<Contract> contract) {
@@ -184,26 +194,32 @@ public class FeignClientProperties {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FeignClientConfiguration that = (FeignClientConfiguration) o;
return loggerLevel == that.loggerLevel &&
Objects.equals(connectTimeout, that.connectTimeout) &&
Objects.equals(readTimeout, that.readTimeout) &&
Objects.equals(retryer, that.retryer) &&
Objects.equals(errorDecoder, that.errorDecoder) &&
Objects.equals(requestInterceptors, that.requestInterceptors) &&
Objects.equals(decode404, that.decode404) &&
Objects.equals(encoder, that.encoder) &&
Objects.equals(decoder, that.decoder) &&
Objects.equals(contract, that.contract);
return this.loggerLevel == that.loggerLevel
&& Objects.equals(this.connectTimeout, that.connectTimeout)
&& Objects.equals(this.readTimeout, that.readTimeout)
&& Objects.equals(this.retryer, that.retryer)
&& Objects.equals(this.errorDecoder, that.errorDecoder)
&& Objects.equals(this.requestInterceptors, that.requestInterceptors)
&& Objects.equals(this.decode404, that.decode404)
&& Objects.equals(this.encoder, that.encoder)
&& Objects.equals(this.decoder, that.decoder)
&& Objects.equals(this.contract, that.contract);
}
@Override
public int hashCode() {
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer,
errorDecoder, requestInterceptors, decode404, encoder, decoder, contract);
return Objects.hash(this.loggerLevel, this.connectTimeout, this.readTimeout,
this.retryer, this.errorDecoder, this.requestInterceptors,
this.decode404, this.encoder, this.decoder, this.contract);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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,11 +16,11 @@
package org.springframework.cloud.openfeign;
import org.springframework.cloud.context.named.NamedContextFactory;
import java.util.Arrays;
import java.util.Objects;
import org.springframework.cloud.context.named.NamedContextFactory;
/**
* @author Dave Syer
* @author Gregor Zurowski
@@ -31,15 +31,16 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
private Class<?>[] configuration;
public FeignClientSpecification() {}
FeignClientSpecification() {
}
public FeignClientSpecification(String name, Class<?>[] configuration) {
FeignClientSpecification(String name, Class<?>[] configuration) {
this.name = name;
this.configuration = configuration;
}
public String getName() {
return name;
return this.name;
}
public void setName(String name) {
@@ -47,7 +48,7 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
}
public Class<?>[] getConfiguration() {
return configuration;
return this.configuration;
}
public void setConfiguration(Class<?>[] configuration) {
@@ -56,24 +57,27 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FeignClientSpecification that = (FeignClientSpecification) o;
return Objects.equals(name, that.name) &&
Arrays.equals(configuration, that.configuration);
return Objects.equals(this.name, that.name)
&& Arrays.equals(this.configuration, that.configuration);
}
@Override
public int hashCode() {
return Objects.hash(name, configuration);
return Objects.hash(this.name, this.configuration);
}
@Override
public String toString() {
return new StringBuilder("FeignClientSpecification{")
.append("name='").append(name).append("', ")
.append("configuration=").append(Arrays.toString(configuration))
.append("}").toString();
return new StringBuilder("FeignClientSpecification{").append("name='")
.append(this.name).append("', ").append("configuration=")
.append(Arrays.toString(this.configuration)).append("}").toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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;
@@ -20,6 +19,16 @@ package org.springframework.cloud.openfeign;
import java.util.ArrayList;
import java.util.List;
import com.netflix.hystrix.HystrixCommand;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.hystrix.HystrixFeign;
import feign.optionals.OptionalDecoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -37,17 +46,6 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
import com.netflix.hystrix.HystrixCommand;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.hystrix.HystrixFeign;
import feign.optionals.OptionalDecoder;
/**
* @author Dave Syer
* @author Venil Noronha
@@ -70,7 +68,8 @@ public class FeignClientsConfiguration {
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
return new OptionalDecoder(
new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
@Bean
@@ -88,24 +87,12 @@ public class FeignClientsConfiguration {
@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
@@ -122,7 +109,21 @@ public class FeignClientsConfiguration {
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(logger);
return new DefaultFeignLoggerFactory(this.logger);
}
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -58,8 +58,8 @@ import org.springframework.util.StringUtils;
* @author Venil Noronha
* @author Gang Li
*/
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
// patterned after Spring Integration IntegrationComponentScanRegistrar
// and RibbonClientsConfigurationRegistgrar
@@ -68,7 +68,68 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
private Environment environment;
public FeignClientsRegistrar() {
FeignClientsRegistrar() {
}
static void validateFallback(final Class clazz) {
Assert.isTrue(!clazz.isInterface(),
"Fallback class must implement the interface annotated by @FeignClient");
}
static void validateFallbackFactory(final Class clazz) {
Assert.isTrue(!clazz.isInterface(), "Fallback factory must produce instances "
+ "of fallback classes that implement the interface annotated by @FeignClient");
}
static String getName(String name) {
if (!StringUtils.hasText(name)) {
return "";
}
String host = null;
try {
String url;
if (!name.startsWith("http://") && !name.startsWith("https://")) {
url = "http://" + name;
}
else {
url = name;
}
host = new URI(url).getHost();
}
catch (URISyntaxException e) {
}
Assert.state(host != null, "Service id not legal hostname (" + name + ")");
return name;
}
static String getUrl(String url) {
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
if (!url.contains("://")) {
url = "http://" + url;
}
try {
new URL(url);
}
catch (MalformedURLException e) {
throw new IllegalArgumentException(url + " is malformed", e);
}
}
return url;
}
static String getPath(String path) {
if (StringUtils.hasText(path)) {
path = path.trim();
if (!path.startsWith("/")) {
path = "/" + path;
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
}
return path;
}
@Override
@@ -182,7 +243,8 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
// null
beanDefinition.setPrimary(primary);
@@ -204,19 +266,6 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
validateFallbackFactory(annotation.getClass("fallbackFactory"));
}
static void validateFallback(final Class clazz) {
Assert.isTrue(
!clazz.isInterface(),
"Fallback class must implement the interface annotated by @FeignClient"
);
}
static void validateFallbackFactory(final Class clazz) {
Assert.isTrue(!clazz.isInterface(),
"Fallback factory must produce instances of fallback classes that implement the interface annotated by @FeignClient"
);
}
/* for testing */ String getName(Map<String, Object> attributes) {
String name = (String) attributes.get("serviceId");
if (!StringUtils.hasText(name)) {
@@ -239,28 +288,6 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
return getName(contextId);
}
static String getName(String name) {
if (!StringUtils.hasText(name)) {
return "";
}
String host = null;
try {
String url;
if (!name.startsWith("http://") && !name.startsWith("https://")) {
url = "http://" + name;
} else {
url = name;
}
host = new URI(url).getHost();
}
catch (URISyntaxException e) {
}
Assert.state(host != null, "Service id not legal hostname (" + name + ")");
return name;
}
private String resolve(String value) {
if (StringUtils.hasText(value)) {
return this.environment.resolvePlaceholders(value);
@@ -273,43 +300,16 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
return getUrl(url);
}
static String getUrl(String url) {
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
if (!url.contains("://")) {
url = "http://" + url;
}
try {
new URL(url);
}
catch (MalformedURLException e) {
throw new IllegalArgumentException(url + " is malformed", e);
}
}
return url;
}
private String getPath(Map<String, Object> attributes) {
String path = resolve((String) attributes.get("path"));
return getPath(path);
}
static String getPath(String path) {
if (StringUtils.hasText(path)) {
path = path.trim();
if (!path.startsWith("/")) {
path = "/" + path;
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
}
return path;
}
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
@@ -346,7 +346,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
}
return basePackages;
}
private String getQualifier(Map<String, Object> client) {
if (client == null) {
return null;
@@ -408,10 +408,9 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
/**
* Creates a new {@link AllTypeFilter} to match if all the given delegates match.
*
* @param delegates must not be {@literal null}.
*/
public AllTypeFilter(List<TypeFilter> delegates) {
AllTypeFilter(List<TypeFilter> delegates) {
Assert.notNull(delegates, "This argument is required, it must not be null");
this.delegates = delegates;
}
@@ -428,5 +427,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
return true;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 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.
@@ -27,10 +27,9 @@ public interface FeignLoggerFactory {
/**
* Factory method to provide a {@link Logger} for a given {@link Class}.
*
* @param type the {@link Class} for which a {@link Logger} instance is to be created
* @return a {@link Logger} instance
*/
public Logger create(Class<?> type);
Logger create(Class<?> type);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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;
@@ -31,66 +30,70 @@ import feign.hystrix.SetterFactory;
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder, fallback);
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder, Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
return builder.target(target, fallbackInstance);
}
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context,
Class<?> beanType, Class<T> targetType) {
private <T> T getFromContext(String fallbackMechanism, String feignClientName,
FeignContext context, Class<?> beanType, Class<T> targetType) {
Object fallbackInstance = context.getInstance(feignClientName, beanType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No " + fallbackMechanism + " instance of type %s found for feign client %s",
beanType, feignClientName));
"No " + fallbackMechanism
+ " instance of type %s found for feign client %s",
beanType, feignClientName));
}
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(
String.format(
"Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
throw new IllegalStateException(String.format("Incompatible "
+ fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
return (T) fallbackInstance;
}
private <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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,14 +16,15 @@
package org.springframework.cloud.openfeign;
import feign.QueryMap;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import feign.QueryMap;
import org.springframework.core.annotation.AliasFor;
/**
* Spring MVC equivalent of OpenFeign's {@link feign.QueryMap} parameter annotation.
*
@@ -32,22 +33,21 @@ import java.lang.annotation.Target;
* @see org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@Target({ ElementType.PARAMETER })
public @interface SpringQueryMap {
/**
* Alias for {@link #encoded()}.
*
* @see QueryMap#encoded()
*/
@AliasFor("encoded")
boolean value() default false;
/**
* @see QueryMap#encoded()
* @return alias for {@link #encoded()}.
*/
@AliasFor("encoded")
boolean value() default false;
/**
* @see QueryMap#encoded()
* @return Specifies whether parameter names and values are already encoded.
*/
@AliasFor("value")
boolean encoded() default false;
/**
* Specifies whether parameter names and values are already encoded.
*
* @see QueryMap#encoded()
*/
@AliasFor("value")
boolean encoded() default false;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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;
@@ -24,6 +23,8 @@ import feign.Target;
* @author Spencer Gibb
*/
interface Targeter {
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target);
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -21,11 +21,11 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.PathVariable;
import feign.MethodMetadata;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
@@ -46,10 +46,12 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
String name = ANNOTATION.cast(annotation).value();
checkState(emptyToNull(name) != null,
"PathVariable annotation was empty on param %s.", context.getParameterIndex());
"PathVariable annotation was empty on param %s.",
context.getParameterIndex());
context.setParameterName(name);
MethodMetadata data = context.getMethodMetadata();
@@ -74,4 +76,5 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess
}
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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,13 +16,14 @@
package org.springframework.cloud.openfeign.annotation;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.SpringQueryMap;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.SpringQueryMap;
/**
* {@link SpringQueryMap} parameter processor.
*
@@ -39,7 +40,8 @@ public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int paramIndex = context.getParameterIndex();
MethodMetadata metadata = context.getMethodMetadata();
if (metadata.queryMapIndex() == null) {
@@ -48,4 +50,5 @@ public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
}
return true;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -21,11 +21,11 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.RequestHeader;
import feign.MethodMetadata;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
@@ -46,13 +46,15 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
if (Map.class.isAssignableFrom(parameterType)) {
checkState(data.headerMapIndex() == null, "Header map can only be present once.");
checkState(data.headerMapIndex() == null,
"Header map can only be present once.");
data.headerMapIndex(parameterIndex);
return true;
@@ -63,8 +65,10 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces
"RequestHeader.value() was empty on parameter %s", parameterIndex);
context.setParameterName(name);
Collection<String> header = context.setTemplateParameter(name, data.template().headers().get(name));
Collection<String> header = context.setTemplateParameter(name,
data.template().headers().get(name));
data.template().header(name, header);
return true;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -21,14 +21,14 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import feign.MethodMetadata;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.web.bind.annotation.RequestParam;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
import feign.MethodMetadata;
/**
* {@link RequestParam} parameter processor.
*
@@ -46,13 +46,15 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
public boolean processArgument(AnnotatedParameterContext context,
Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
if (Map.class.isAssignableFrom(parameterType)) {
checkState(data.queryMapIndex() == null, "Query map can only be present once.");
checkState(data.queryMapIndex() == null,
"Query map can only be present once.");
data.queryMapIndex(parameterIndex);
return true;
@@ -61,8 +63,7 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
RequestParam requestParam = ANNOTATION.cast(annotation);
String name = requestParam.value();
checkState(emptyToNull(name) != null,
"RequestParam.value() was empty on parameter %s",
parameterIndex);
"RequestParam.value() was empty on parameter %s", parameterIndex);
context.setParameterName(name);
Collection<String> query = context.setTemplateParameter(name,
@@ -70,4 +71,5 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
data.template().query(name, query);
return true;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign.encoding;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.util.Assert;
/**
@@ -34,7 +35,6 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
/**
* Creates new instance of {@link BaseRequestInterceptor}.
*
* @param properties the encoding properties
*/
protected BaseRequestInterceptor(FeignClientEncodingProperties properties) {
@@ -44,12 +44,12 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
/**
* Adds the header if it wasn't yet specified.
*
* @param requestTemplate the request
* @param name the header name
* @param values the header values
* @param name the header name
* @param values the header values
*/
protected void addHeader(RequestTemplate requestTemplate, String name, String... values) {
protected void addHeader(RequestTemplate requestTemplate, String name,
String... values) {
if (!requestTemplate.headers().containsKey(name)) {
requestTemplate.header(name, values);
@@ -57,7 +57,7 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
}
protected FeignClientEncodingProperties getProperties() {
return properties;
return this.properties;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -40,14 +40,16 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(Client.class)
@ConditionalOnProperty(value = "feign.compression.response.enabled", matchIfMissing = false)
//The OK HTTP client uses "transparent" compression.
//If the accept-encoding header is present it disable transparent compression
// The OK HTTP client uses "transparent" compression.
// If the accept-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignAcceptGzipEncodingAutoConfiguration {
@Bean
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(
FeignClientEncodingProperties properties) {
return new FeignAcceptGzipEncodingInterceptor(properties);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -19,10 +19,11 @@ package org.springframework.cloud.openfeign.encoding;
import feign.RequestTemplate;
/**
* Enables the HTTP response payload compression by specifying the {@code Accept-Encoding} headers.
* Although this does not yet mean that the requests will be compressed, it requires the remote server
* to understand the header and be configured to compress responses. Still no all responses might be compressed
* based on the media type matching and other factors like the response content length.
* Enables the HTTP response payload compression by specifying the {@code Accept-Encoding}
* headers. Although this does not yet mean that the requests will be compressed, it
* requires the remote server to understand the header and be configured to compress
* responses. Still no all responses might be compressed based on the media type matching
* and other factors like the response content length.
*
* @author Jakub Narloch
*/
@@ -30,10 +31,10 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
/**
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
protected FeignAcceptGzipEncodingInterceptor(
FeignClientEncodingProperties properties) {
super(properties);
}
@@ -43,7 +44,8 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
@Override
public void apply(RequestTemplate template) {
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER,
HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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,11 +16,11 @@
package org.springframework.cloud.openfeign.encoding;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Arrays;
import java.util.Objects;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* The Feign encoding properties.
*
@@ -32,7 +32,8 @@ public class FeignClientEncodingProperties {
/**
* The list of supported mime types.
*/
private String[] mimeTypes = new String[]{"text/xml", "application/xml", "application/json"};
private String[] mimeTypes = new String[] { "text/xml", "application/xml",
"application/json" };
/**
* The minimum threshold content size.
@@ -40,7 +41,7 @@ public class FeignClientEncodingProperties {
private int minRequestSize = 2048;
public String[] getMimeTypes() {
return mimeTypes;
return this.mimeTypes;
}
public void setMimeTypes(String[] mimeTypes) {
@@ -48,7 +49,7 @@ public class FeignClientEncodingProperties {
}
public int getMinRequestSize() {
return minRequestSize;
return this.minRequestSize;
}
public void setMinRequestSize(int minRequestSize) {
@@ -57,24 +58,28 @@ public class FeignClientEncodingProperties {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
FeignClientEncodingProperties that = (FeignClientEncodingProperties) o;
return Arrays.equals(mimeTypes, that.mimeTypes) &&
Objects.equals(minRequestSize, that.minRequestSize);
return Arrays.equals(this.mimeTypes, that.mimeTypes)
&& Objects.equals(this.minRequestSize, that.minRequestSize);
}
@Override
public int hashCode() {
return Objects.hash(mimeTypes, minRequestSize);
return Objects.hash(this.mimeTypes, this.minRequestSize);
}
@Override
public String toString() {
return new StringBuilder("FeignClientEncodingProperties{")
.append("mimeTypes=").append(Arrays.toString(mimeTypes)).append(", ")
.append("minRequestSize=").append(minRequestSize)
.append("}").toString();
return new StringBuilder("FeignClientEncodingProperties{").append("mimeTypes=")
.append(Arrays.toString(this.mimeTypes)).append(", ")
.append("minRequestSize=").append(this.minRequestSize).append("}")
.toString();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -39,15 +39,17 @@ import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(Client.class)
//The OK HTTP client uses "transparent" compression.
//If the content-encoding header is present it disable transparent compression
// The OK HTTP client uses "transparent" compression.
// If the content-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@ConditionalOnProperty(value = "feign.compression.request.enabled", matchIfMissing = false)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignContentGzipEncodingAutoConfiguration {
@Bean
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(
FeignClientEncodingProperties properties) {
return new FeignContentGzipEncodingInterceptor(properties);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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,13 +16,14 @@
package org.springframework.cloud.openfeign.encoding;
import feign.RequestTemplate;
import java.util.Collection;
import java.util.Map;
import feign.RequestTemplate;
/**
* Enables the HTTP request payload compression by specifying the {@code Content-Encoding} headers.
* Enables the HTTP request payload compression by specifying the {@code Content-Encoding}
* headers.
*
* @author Jakub Narloch
*/
@@ -30,10 +31,10 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
/**
* Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
*
* @param properties the encoding properties
*/
protected FeignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
protected FeignContentGzipEncodingInterceptor(
FeignClientEncodingProperties properties) {
super(properties);
}
@@ -44,14 +45,13 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
public void apply(RequestTemplate template) {
if (requiresCompression(template)) {
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
HttpEncoding.DEFLATE_ENCODING);
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER,
HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
}
}
/**
* Returns whether the request requires GZIP compression.
*
* @param template the request template
* @return true if request requires compression, false otherwise
*/
@@ -64,7 +64,6 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
/**
* Returns whether the request content length exceed configured minimum size.
*
* @param contentLength the content length header value
* @return true if length is grater than minimum size, false otherwise
*/
@@ -78,14 +77,14 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
final String strLen = contentLength.iterator().next();
final long length = Long.parseLong(strLen);
return length > getProperties().getMinRequestSize();
} catch (NumberFormatException ex) {
}
catch (NumberFormatException ex) {
return false;
}
}
/**
* Returns whether the content mime types matches the configures mime types.
*
* @param contentTypes the content types
* @return true if any specified content type matches the request content types
*/
@@ -94,7 +93,8 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
return false;
}
if (getProperties().getMimeTypes() == null || getProperties().getMimeTypes().length == 0) {
if (getProperties().getMimeTypes() == null
|| getProperties().getMimeTypes().length == 0) {
// no specific mime types has been set - matching everything
return true;
}
@@ -107,4 +107,5 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -52,4 +52,5 @@ public interface HttpEncoding {
* The Deflate encoding.
*/
String DEFLATE_ENCODING = "deflate";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -18,14 +18,14 @@ package org.springframework.cloud.openfeign.ribbon;
import java.util.Map;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.util.ConcurrentReferenceHashMap;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
/**
* Factory for SpringLoadBalancer instances that caches the entries created.
*
@@ -37,6 +37,7 @@ import com.netflix.loadbalancer.ILoadBalancer;
public class CachingSpringLoadBalancerFactory {
protected final SpringClientFactory factory;
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
@@ -45,23 +46,27 @@ public class CachingSpringLoadBalancerFactory {
this.factory = factory;
}
public CachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
this.factory = factory;
this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
}
public FeignLoadBalancer create(String clientName) {
FeignLoadBalancer client = this.cache.get(clientName);
if(client != null) {
if (client != null) {
return client;
}
IClientConfig config = this.factory.getClientConfig(clientName);
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
ServerIntrospector.class);
client = this.loadBalancedRetryFactory != null
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
this.loadBalancedRetryFactory)
: new FeignLoadBalancer(lb, config, serverIntrospector);
this.cache.put(clientName, client);
return client;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,12 +12,12 @@
* 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.ribbon;
import feign.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
@@ -28,11 +28,13 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -57,13 +57,17 @@ public class FeignLoadBalancer extends
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
private final RibbonProperties ribbon;
protected int connectTimeout;
protected int readTimeout;
protected IClientConfig clientConfig;
protected ServerIntrospector serverIntrospector;
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector) {
ServerIntrospector serverIntrospector) {
super(lb, clientConfig);
this.setRetryHandler(RetryHandler.DEFAULT);
this.clientConfig = clientConfig;
@@ -80,8 +84,7 @@ public class FeignLoadBalancer extends
Request.Options options;
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(
override.connectTimeout(this.connectTimeout),
options = new Request.Options(override.connectTimeout(this.connectTimeout),
override.readTimeout(this.readTimeout));
}
else {
@@ -110,13 +113,15 @@ public class FeignLoadBalancer extends
@Override
public URI reconstructURIWithServer(Server server, URI original) {
URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig, this.serverIntrospector, server);
URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig,
this.serverIntrospector, server);
return super.reconstructURIWithServer(server, uri);
}
protected static class RibbonRequest extends ClientRequest implements Cloneable {
private final Request request;
private final Client client;
protected RibbonRequest(Client client, Request request, URI uri) {
@@ -161,8 +166,9 @@ public class FeignLoadBalancer extends
@Override
public HttpHeaders getHeaders() {
Map<String, List<String>> headers = new HashMap<>();
Map<String, Collection<String>> feignHeaders = RibbonRequest.this.toRequest().headers();
for(String key : feignHeaders.keySet()) {
Map<String, Collection<String>> feignHeaders = RibbonRequest.this
.toRequest().headers();
for (String key : feignHeaders.keySet()) {
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
}
HttpHeaders httpHeaders = new HttpHeaders();
@@ -174,22 +180,24 @@ public class FeignLoadBalancer extends
}
public Request getRequest() {
return request;
return this.request;
}
public Client getClient() {
return client;
return this.client;
}
@Override
public Object clone() {
return new RibbonRequest(this.client, this.request, getUri());
}
}
protected static class RibbonResponse implements IResponse {
private final URI uri;
private final Response response;
protected RibbonResponse(URI uri, Response response) {
@@ -235,4 +243,4 @@ public class FeignLoadBalancer extends
}
}
}

View File

@@ -1,19 +1,17 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* * Copyright 2013-2016 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
* *
* * http://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.
* 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
*
* http://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.ribbon;
@@ -34,9 +32,13 @@ import org.springframework.retry.RetryContext;
* @author Ryan Baxter
*/
public class FeignRetryPolicy extends InterceptorRetryPolicy {
private HttpRequest request;
private String serviceId;
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy,
ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
super(request, policy, serviceInstanceChooser, serviceName);
this.request = request;
this.serviceId = serviceName;
@@ -45,17 +47,18 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy {
@Override
public boolean canRetry(RetryContext context) {
/*
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server if one is not
* set in the retry context and then return true. RetryTemplat calls the canRetry method of
* the policy even on its first execution. So the fact that we didnt have a service instance set
* in the RetryContext signaled that it was the first execution and we should return true.
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server
* if one is not set in the retry context and then return true. RetryTemplat calls
* the canRetry method of the policy even on its first execution. So the fact that
* we didnt have a service instance set in the RetryContext signaled that it was
* the first execution and we should return true.
*
* In the Feign scenario, Feign as actually already queried the load balancer for a service instance
* and we set that service instance in the context when we call the open method of the policy. So in
* the Feign case we just return true if the retry count is 0 indicating we haven't yet made a failed
* request.
* In the Feign scenario, Feign as actually already queried the load balancer for
* a service instance and we set that service instance in the context when we call
* the open method of the policy. So in the Feign case we just return true if the
* retry count is 0 indicating we haven't yet made a failed request.
*/
if(context.getRetryCount() == 0) {
if (context.getRetryCount() == 0) {
return true;
}
return super.canRetry(context);
@@ -64,20 +67,25 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy {
@Override
public RetryContext open(RetryContext parent) {
/*
* With Feign (unlike Ribbon) the request already has the URI for the service instance
* we are going to make the request to, so extract that information and set the service
* instance in the context. In the Ribbon scenario the URI in the request object still has
* the service id so we choose and set the service instance later on.
* With Feign (unlike Ribbon) the request already has the URI for the service
* instance we are going to make the request to, so extract that information and
* set the service instance in the context. In the Ribbon scenario the URI in the
* request object still has the service id so we choose and set the service
* instance later on.
*/
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent, this.request);
context.setServiceInstance(new FeignRetryPolicyServiceInstance(serviceId, request));
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent,
this.request);
context.setServiceInstance(
new FeignRetryPolicyServiceInstance(this.serviceId, this.request));
return context;
}
class FeignRetryPolicyServiceInstance implements ServiceInstance {
private String serviceId;
private HttpRequest request;
private Map<String, String> metadata;
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
@@ -88,32 +96,34 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy {
@Override
public String getServiceId() {
return serviceId;
return this.serviceId;
}
@Override
public String getHost() {
return request.getURI().getHost();
return this.request.getURI().getHost();
}
@Override
public int getPort() {
return request.getURI().getPort();
return this.request.getURI().getPort();
}
@Override
public boolean isSecure() {
return "https".equals(request.getURI().getScheme());
return "https".equals(this.request.getURI().getScheme());
}
@Override
public URI getUri() {
return request.getURI();
return this.request.getURI();
}
@Override
public Map<String, String> getMetadata() {
return metadata;
return this.metadata;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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,25 +16,24 @@
package org.springframework.cloud.openfeign.ribbon;
import com.netflix.loadbalancer.ILoadBalancer;
import feign.Feign;
import feign.Request;
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.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import com.netflix.loadbalancer.ILoadBalancer;
import feign.Feign;
import feign.Request;
/**
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
* load balancer.
@@ -45,8 +44,9 @@ import feign.Request;
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
@@ -66,8 +66,7 @@ public class FeignRibbonClientAutoConfiguration {
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
SpringClientFactory factory,
LoadBalancedRetryFactory retryFactory) {
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}
@@ -76,4 +75,5 @@ public class FeignRibbonClientAutoConfiguration {
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,31 +12,32 @@
* 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.ribbon;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import java.util.Timer;
import java.util.TimerTask;
import javax.annotation.PreDestroy;
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -48,9 +49,18 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
class HttpClientFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
@Configuration
@ConditionalOnMissingBean(CloseableHttpClient.class)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@@ -65,10 +75,12 @@ class HttpClientFeignLoadBalancedConfiguration {
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
httpClientProperties.getTimeToLiveUnit(),
this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
@@ -80,49 +92,47 @@ class HttpClientFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true")
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties);
public CloseableHttpClient customHttpClient(
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
HttpClientBuilder builder = HttpClientBuilder.create()
.disableCookieManagement().useSystemProperties();
this.httpClient = createClient(builder, httpClientConnectionManager,
httpClientProperties);
return this.httpClient;
}
@Bean
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true)
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, httpClientProperties);
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
this.httpClient = createClient(httpClientFactory.createBuilder(),
httpClientConnectionManager, httpClientProperties);
return this.httpClient;
}
private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
private CloseableHttpClient createClient(HttpClientBuilder builder,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig).
setConnectionManager(httpClientConnectionManager).build();
CloseableHttpClient httpClient = builder
.setDefaultRequestConfig(defaultRequestConfig)
.setConnectionManager(httpClientConnectionManager).build();
return httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -19,13 +19,10 @@ package org.springframework.cloud.openfeign.ribbon;
import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import com.netflix.client.ClientException;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import feign.Client;
import feign.Request;
import feign.Response;
@@ -41,17 +38,37 @@ public class LoadBalancerFeignClient implements Client {
static final Request.Options DEFAULT_OPTIONS = new Request.Options();
private final Client delegate;
private CachingSpringLoadBalancerFactory lbClientFactory;
private SpringClientFactory clientFactory;
public LoadBalancerFeignClient(Client delegate,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) {
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) {
this.delegate = delegate;
this.lbClientFactory = lbClientFactory;
this.clientFactory = clientFactory;
}
static URI cleanUrl(String originalUrl, String host) {
String newUrl = originalUrl;
if (originalUrl.startsWith("https://")) {
newUrl = originalUrl.substring(0, 8)
+ originalUrl.substring(8 + host.length());
}
else if (originalUrl.startsWith("http")) {
newUrl = originalUrl.substring(0, 7)
+ originalUrl.substring(7 + host.length());
}
StringBuffer buffer = new StringBuffer(newUrl);
if ((newUrl.startsWith("https://") && newUrl.length() == 8)
|| (newUrl.startsWith("http://") && newUrl.length() == 7)) {
buffer.append("/");
}
return URI.create(buffer.toString());
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
@@ -62,8 +79,8 @@ public class LoadBalancerFeignClient implements Client {
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
requestConfig).toResponse();
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
@@ -78,7 +95,8 @@ public class LoadBalancerFeignClient implements Client {
IClientConfig requestConfig;
if (options == DEFAULT_OPTIONS) {
requestConfig = this.clientFactory.getClientConfig(clientName);
} else {
}
else {
requestConfig = new FeignOptionsClientConfig(options);
}
return requestConfig;
@@ -98,28 +116,13 @@ public class LoadBalancerFeignClient implements Client {
return this.delegate;
}
static URI cleanUrl(String originalUrl, String host) {
String newUrl = originalUrl;
if(originalUrl.startsWith("https://")) {
newUrl = originalUrl.substring(0, 8) + originalUrl.substring(8 + host.length());
} else if(originalUrl.startsWith("http")) {
newUrl = originalUrl.substring(0, 7) + originalUrl.substring(7 + host.length());
}
StringBuffer buffer = new StringBuffer(newUrl);
if((newUrl.startsWith("https://") && newUrl.length() == 8) ||
(newUrl.startsWith("http://") && newUrl.length() == 7)) {
buffer.append("/");
}
return URI.create(buffer.toString());
}
private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
public FeignOptionsClientConfig(Request.Options options) {
FeignOptionsClientConfig(Request.Options options) {
setProperty(CommonClientConfigKey.ConnectTimeout,
options.connectTimeoutMillis());
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
@@ -136,4 +139,5 @@ public class LoadBalancerFeignClient implements Client {
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,24 +12,25 @@
* 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.ribbon;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import feign.Client;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,18 +39,28 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
@ConditionalOnProperty("feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
@Configuration
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
public ConnectionPool httpClientConnectionPool(
FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
@@ -58,30 +69,26 @@ class OkHttpFeignLoadBalancedConfiguration {
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
ConnectionPool connectionPool,
FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
this.okHttpClient = httpClientFactory
.createBuilder(httpClientProperties.isDisableSslValidation())
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects).connectionPool(connectionPool)
.build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
if (this.okHttpClient != null) {
this.okHttpClient.dispatcher().executorService().shutdown();
this.okHttpClient.connectionPool().evictAll();
}
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}

View File

@@ -1,28 +1,32 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* * Copyright 2013-2016 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
* *
* * http://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.
* 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
*
* http://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.ribbon;
import java.io.IOException;
import java.net.URI;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import feign.Request;
import feign.Response;
import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
@@ -40,71 +44,82 @@ import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.StreamUtils;
import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.client.RequestSpecificRetryHandler;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
/**
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
*
* @author Ryan Baxter
* @author Gang Li
*/
public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements ServiceInstanceChooser {
public class RetryableFeignLoadBalancer extends FeignLoadBalancer
implements ServiceInstanceChooser {
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
ServerIntrospector serverIntrospector,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
super(lb, clientConfig, serverIntrospector);
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
}
@Override
public RibbonResponse execute(final RibbonRequest request, IClientConfig configOverride)
throws IOException {
public RibbonResponse execute(final RibbonRequest request,
IClientConfig configOverride) throws IOException {
final Request.Options options;
if (configOverride != null) {
RibbonProperties ribbon = RibbonProperties.from(configOverride);
options = new Request.Options(
ribbon.connectTimeout(this.connectTimeout),
options = new Request.Options(ribbon.connectTimeout(this.connectTimeout),
ribbon.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(this.getClientName(), this);
final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory
.createRetryPolicy(this.getClientName(), this);
RetryTemplate retryTemplate = new RetryTemplate();
BackOffPolicy backOffPolicy = loadBalancedRetryFactory.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryFactory.createRetryListeners(this.getClientName());
BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory
.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(
backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryFactory
.createRetryListeners(this.getClientName());
if (retryListeners != null && retryListeners.length != 0) {
retryTemplate.setListeners(retryListeners);
}
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName()));
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this,
this.getClientName()));
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
@Override
public RibbonResponse doWithRetry(RetryContext retryContext) throws IOException {
public RibbonResponse doWithRetry(RetryContext retryContext)
throws IOException {
Request feignRequest = null;
//on retries the policy will choose the server and set it in the context
//extract the server and update the request being made
// on retries the policy will choose the server and set it in the context
// extract the server and update the request being made
if (retryContext instanceof LoadBalancedRetryContext) {
ServiceInstance service = ((LoadBalancedRetryContext) retryContext).getServiceInstance();
ServiceInstance service = ((LoadBalancedRetryContext) retryContext)
.getServiceInstance();
if (service != null) {
feignRequest = ((RibbonRequest) request.replaceUri(reconstructURIWithServer(new Server(service.getHost(), service.getPort()), request.getUri()))).toRequest();
feignRequest = ((RibbonRequest) request
.replaceUri(reconstructURIWithServer(
new Server(service.getHost(), service.getPort()),
request.getUri()))).toRequest();
}
}
if (feignRequest == null) {
feignRequest = request.toRequest();
}
Response response = request.client().execute(feignRequest, options);
if (retryPolicy != null && retryPolicy.retryableStatusCode(response.status())) {
byte[] byteArray = response.body() == null ? new byte[]{} : StreamUtils.copyToByteArray(response.body().asInputStream());
if (retryPolicy != null
&& retryPolicy.retryableStatusCode(response.status())) {
byte[] byteArray = response.body() == null ? new byte[] {}
: StreamUtils
.copyToByteArray(response.body().asInputStream());
response.close();
throw new RibbonResponseStatusCodeException(RetryableFeignLoadBalancer.this.clientName, response,
throw new RibbonResponseStatusCodeException(
RetryableFeignLoadBalancer.this.clientName, response,
byteArray, request.getUri());
}
return new RibbonResponse(request.getUri(), response);
@@ -120,7 +135,8 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig);
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(),
requestConfig);
}
@Override
@@ -128,4 +144,5 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
this.getLoadBalancer().chooseServer(serviceId));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.ribbon;
import java.io.ByteArrayInputStream;
@@ -23,22 +24,26 @@ import feign.Response;
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
/**
* A {@link RetryableStatusCodeException} for {@link Response}s
* A {@link RetryableStatusCodeException} for {@link Response}s.
*
* @author Ryan Baxter
*/
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
private Response response;
public RibbonResponseStatusCodeException(String serviceId, Response response, byte[] body, URI uri) {
private final Response response;
public RibbonResponseStatusCodeException(String serviceId, Response response,
byte[] body, URI uri) {
super(serviceId, response.status(), response, uri);
this.response = Response.builder().body(new ByteArrayInputStream(body), body.length)
this.response = Response.builder()
.body(new ByteArrayInputStream(body), body.length)
.headers(response.headers()).reason(response.reason())
.status(response.status()).request(response.request()).build();
}
@Override
public Response getResponse() {
return response;
return this.response;
}
}

View File

@@ -1,3 +1,19 @@
/*
* Copyright 2013-2019 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
*
* http://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.support;
import com.netflix.hystrix.HystrixCommand;
@@ -6,8 +22,11 @@ import com.netflix.hystrix.HystrixThreadPoolKey;
/**
* Convenience class for implementing feign fallbacks that return {@link HystrixCommand}.
* Also useful for return types of {@link rx.Observable} and {@link java.util.concurrent.Future}.
* For those return types, just call {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively.
* Also useful for return types of {@link rx.Observable} and
* {@link java.util.concurrent.Future}. For those return types, just call
* {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively.
*
* @param <T> result type
* @author Spencer Gibb
*/
public class FallbackCommand<T> extends HystrixCommand<T> {
@@ -28,17 +47,21 @@ public class FallbackCommand<T> extends HystrixCommand<T> {
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
public FallbackCommand(T result, HystrixCommandGroupKey group,
int executionIsolationThreadTimeoutInMilliseconds) {
super(group, executionIsolationThreadTimeoutInMilliseconds);
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
public FallbackCommand(T result, HystrixCommandGroupKey group,
HystrixThreadPoolKey threadPool) {
super(group, threadPool);
this.result = result;
}
public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
public FallbackCommand(T result, HystrixCommandGroupKey group,
HystrixThreadPoolKey threadPool,
int executionIsolationThreadTimeoutInMilliseconds) {
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
this.result = result;
}
@@ -52,4 +75,5 @@ public class FallbackCommand<T> extends HystrixCommand<T> {
protected T run() throws Exception {
return this.result;
}
}

View File

@@ -1,24 +1,23 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* * Copyright 2013-2016 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
* *
* * http://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.
* 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
*
* http://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.support;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@@ -26,26 +25,65 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
*/
@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {
/**
* Default value for disabling SSL validation.
*/
public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
/**
* Default value for max number od connections.
*/
public static final int DEFAULT_MAX_CONNECTIONS = 200;
/**
* Default value for max number od connections per route.
*/
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
/**
* Default value for time to live.
*/
public static final long DEFAULT_TIME_TO_LIVE = 900L;
/**
* Default time to live unit.
*/
public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
/**
* Default value for following redirects.
*/
public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
/**
* Default value for connection timeout.
*/
public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
/**
* Default value for connection timer repeat.
*/
public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
private long timeToLive = DEFAULT_TIME_TO_LIVE;
private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
public int getConnectionTimerRepeat() {
return connectionTimerRepeat;
return this.connectionTimerRepeat;
}
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
@@ -53,7 +91,7 @@ public class FeignHttpClientProperties {
}
public boolean isDisableSslValidation() {
return disableSslValidation;
return this.disableSslValidation;
}
public void setDisableSslValidation(boolean disableSslValidation) {
@@ -61,7 +99,7 @@ public class FeignHttpClientProperties {
}
public int getMaxConnections() {
return maxConnections;
return this.maxConnections;
}
public void setMaxConnections(int maxConnections) {
@@ -69,7 +107,7 @@ public class FeignHttpClientProperties {
}
public int getMaxConnectionsPerRoute() {
return maxConnectionsPerRoute;
return this.maxConnectionsPerRoute;
}
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
@@ -77,7 +115,7 @@ public class FeignHttpClientProperties {
}
public long getTimeToLive() {
return timeToLive;
return this.timeToLive;
}
public void setTimeToLive(long timeToLive) {
@@ -85,7 +123,7 @@ public class FeignHttpClientProperties {
}
public TimeUnit getTimeToLiveUnit() {
return timeToLiveUnit;
return this.timeToLiveUnit;
}
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
@@ -93,7 +131,7 @@ public class FeignHttpClientProperties {
}
public boolean isFollowRedirects() {
return followRedirects;
return this.followRedirects;
}
public void setFollowRedirects(boolean followRedirects) {
@@ -101,10 +139,11 @@ public class FeignHttpClientProperties {
}
public int getConnectionTimeout() {
return connectionTimeout;
return this.connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -30,7 +30,11 @@ import static java.util.Optional.ofNullable;
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
public class FeignUtils {
public final class FeignUtils {
private FeignUtils() {
throw new IllegalStateException("Can't instantiate a utility class");
}
static HttpHeaders getHttpHeaders(Map<String, Collection<String>> headers) {
HttpHeaders httpHeaders = new HttpHeaders();

View File

@@ -1,3 +1,19 @@
/*
* Copyright 2013-2019 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
*
* http://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.support;
import java.io.IOException;
@@ -5,20 +21,21 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedList;
import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;
/**
* Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via
* composition.
* @author chadjaros
*
* @author chad jaros
*/
public class ResponseEntityDecoder implements Decoder {
@@ -29,12 +46,12 @@ public class ResponseEntityDecoder implements Decoder {
}
@Override
public Object decode(final Response response, Type type) throws IOException,
FeignException {
public Object decode(final Response response, Type type)
throws IOException, FeignException {
if (isParameterizeHttpEntity(type)) {
type = ((ParameterizedType) type).getActualTypeArguments()[0];
Object decodedObject = decoder.decode(response, type);
Object decodedObject = this.decoder.decode(response, type);
return createResponse(decodedObject, response);
}
@@ -42,7 +59,7 @@ public class ResponseEntityDecoder implements Decoder {
return createResponse(null, response);
}
else {
return decoder.decode(response, type);
return this.decoder.decode(response, type);
}
}
@@ -69,7 +86,8 @@ public class ResponseEntityDecoder implements Decoder {
headers.put(key, new LinkedList<>(response.headers().get(key)));
}
return new ResponseEntity<>((T) instance, headers, HttpStatus.valueOf(response
.status()));
return new ResponseEntity<>((T) instance, headers,
HttpStatus.valueOf(response.status()));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,19 +12,21 @@
* 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.support;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.http.HttpHeaders;
@@ -32,10 +34,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.HttpMessageConverterExtractor;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
/**
* @author Spencer Gibb
@@ -63,7 +62,7 @@ public class SpringDecoder implements Decoder {
"type is not an instance of Class or ParameterizedType: " + type);
}
private class FeignResponseAdapter implements ClientHttpResponse {
private final class FeignResponseAdapter implements ClientHttpResponse {
private final Response response;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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.support;
@@ -49,7 +48,7 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead
/**
* @author Spencer Gibb
* @author ScienJus
* @author Scien Jus
*/
public class SpringEncoder implements Encoder {
@@ -79,12 +78,13 @@ public class SpringEncoder implements Encoder {
if (bodyType != null && bodyType.equals(MultipartFile.class)) {
if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
springFormEncoder.encode(requestBody, bodyType, request);
this.springFormEncoder.encode(requestBody, bodyType, request);
return;
} else {
String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA +
"\" not set for request body of type " +
requestBody.getClass().getSimpleName();
}
else {
String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA
+ "\" not set for request body of type "
+ requestBody.getClass().getSimpleName();
throw new EncodeException(message);
}
}
@@ -95,8 +95,8 @@ public class SpringEncoder implements Encoder {
if (log.isDebugEnabled()) {
if (requestContentType != null) {
log.debug("Writing [" + requestBody + "] as \""
+ requestContentType + "\" using ["
+ messageConverter + "]");
+ requestContentType + "\" using [" + messageConverter
+ "]");
}
else {
log.debug("Writing [" + requestBody + "] using ["
@@ -124,14 +124,17 @@ public class SpringEncoder implements Encoder {
Charset charset;
if (messageConverter instanceof ByteArrayHttpMessageConverter) {
charset = null;
} else if (messageConverter instanceof ProtobufHttpMessageConverter &&
ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(outputMessage.getHeaders().getContentType())) {
}
else if (messageConverter instanceof ProtobufHttpMessageConverter
&& ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(
outputMessage.getHeaders().getContentType())) {
charset = null;
} else {
}
else {
charset = StandardCharsets.UTF_8;
}
request.body(Request.Body.encoded(outputMessage.getOutputStream()
.toByteArray(), charset));
request.body(Request.Body.encoded(
outputMessage.getOutputStream().toByteArray(), charset));
return;
}
}
@@ -144,14 +147,14 @@ public class SpringEncoder implements Encoder {
}
}
private class FeignOutputMessage implements HttpOutputMessage {
private final class FeignOutputMessage implements HttpOutputMessage {
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
private final HttpHeaders httpHeaders;
private FeignOutputMessage(RequestTemplate request) {
httpHeaders = getHttpHeaders(request.headers());
this.httpHeaders = getHttpHeaders(request.headers());
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -75,18 +75,22 @@ public class SpringMvcContract extends Contract.BaseContract
private static final String CONTENT_TYPE = "Content-Type";
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR =
TypeDescriptor.valueOf(String.class);
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR =
TypeDescriptor.valueOf(Iterable.class);
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor
.valueOf(String.class);
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor
.valueOf(Iterable.class);
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
private final Map<String, Method> processedMethods = new HashMap<>();
private final ConversionService conversionService;
private final ConvertingExpanderFactory convertingExpanderFactory;
private ResourceLoader resourceLoader = new DefaultResourceLoader();
public SpringMvcContract() {
@@ -117,6 +121,26 @@ public class SpringMvcContract extends Contract.BaseContract
this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
}
private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
Parameter parameter = method.getParameters()[paramIndex];
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter);
// Feign applies the Param.Expander to each element of an Iterable, so in those
// cases we need to provide a TypeDescriptor of the element.
if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
TypeDescriptor elementTypeDescriptor = typeDescriptor
.getElementTypeDescriptor();
checkState(elementTypeDescriptor != null,
"Could not resolve element type of Iterable type %s. Not declared?",
typeDescriptor);
typeDescriptor = elementTypeDescriptor;
}
return typeDescriptor;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
@@ -190,8 +214,7 @@ public class SpringMvcContract extends Contract.BaseContract
if (pathValue != null) {
pathValue = resolve(pathValue);
// Append path from @RequestMapping if value is present on method
if (!pathValue.startsWith("/")
&& !data.template().path().endsWith("/")) {
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().uri(pathValue, true);
@@ -256,9 +279,10 @@ public class SpringMvcContract extends Contract.BaseContract
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
Param.Expander expander =
convertingExpanderFactory.getExpander(typeDescriptor);
if (this.conversionService.canConvert(typeDescriptor,
STRING_TYPE_DESCRIPTOR)) {
Param.Expander expander = this.convertingExpanderFactory
.getExpander(typeDescriptor);
if (expander != null) {
data.indexToExpander().put(paramIndex, expander);
}
@@ -267,26 +291,6 @@ public class SpringMvcContract extends Contract.BaseContract
return isHttpAnnotation;
}
private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
Parameter parameter = method.getParameters()[paramIndex];
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter);
// Feign applies the Param.Expander to each element of an Iterable, so in those
// cases we need to provide a TypeDescriptor of the element.
if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
TypeDescriptor elementTypeDescriptor =
typeDescriptor.getElementTypeDescriptor();
checkState(elementTypeDescriptor != null,
"Could not resolve element type of Iterable type %s. Not declared?",
typeDescriptor);
typeDescriptor = elementTypeDescriptor;
}
return typeDescriptor;
}
private void parseProduces(MethodMetadata md, Method method,
RequestMapping annotation) {
String[] serverProduces = annotation.produces();
@@ -315,7 +319,7 @@ public class SpringMvcContract extends Contract.BaseContract
int index = header.indexOf('=');
if (!header.contains("!=") && index >= 0) {
md.template().header(resolve(header.substring(0, index)),
resolve(header.substring(index + 1).trim()));
resolve(header.substring(index + 1).trim()));
}
}
}
@@ -360,48 +364,14 @@ public class SpringMvcContract extends Contract.BaseContract
parameterAnnotation.annotationType(), null);
}
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, String[] parameterNames) {
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes,
String[] parameterNames) {
// has a parameter name
return parameterNames != null && parameterNames.length > parameterIndex
// has a type
// has a type
&& parameterTypes != null && parameterTypes.length > parameterIndex;
}
private class SimpleAnnotatedParameterContext
implements AnnotatedParameterProcessor.AnnotatedParameterContext {
private final MethodMetadata methodMetadata;
private final int parameterIndex;
public SimpleAnnotatedParameterContext(MethodMetadata methodMetadata,
int parameterIndex) {
this.methodMetadata = methodMetadata;
this.parameterIndex = parameterIndex;
}
@Override
public MethodMetadata getMethodMetadata() {
return this.methodMetadata;
}
@Override
public int getParameterIndex() {
return this.parameterIndex;
}
@Override
public void setParameterName(String name) {
nameParam(this.methodMetadata, name, this.parameterIndex);
}
@Override
public Collection<String> setTemplateParameter(String name,
Collection<String> rest) {
return addTemplateParameter(rest, name);
}
}
/**
* @deprecated Not used internally anymore. Will be removed in the future.
*/
@@ -431,10 +401,48 @@ public class SpringMvcContract extends Contract.BaseContract
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
return value -> {
Object converted = this.conversionService.convert(
value, typeDescriptor, STRING_TYPE_DESCRIPTOR);
Object converted = this.conversionService.convert(value, typeDescriptor,
STRING_TYPE_DESCRIPTOR);
return (String) converted;
};
}
}
private class SimpleAnnotatedParameterContext
implements AnnotatedParameterProcessor.AnnotatedParameterContext {
private final MethodMetadata methodMetadata;
private final int parameterIndex;
SimpleAnnotatedParameterContext(MethodMetadata methodMetadata,
int parameterIndex) {
this.methodMetadata = methodMetadata;
this.parameterIndex = parameterIndex;
}
@Override
public MethodMetadata getMethodMetadata() {
return this.methodMetadata;
}
@Override
public int getParameterIndex() {
return this.parameterIndex;
}
@Override
public void setParameterName(String name) {
nameParam(this.methodMetadata, name, this.parameterIndex);
}
@Override
public Collection<String> setTemplateParameter(String name,
Collection<String> rest) {
return addTemplateParameter(rest, name);
}
}
}

View File

@@ -1,33 +1,36 @@
{"groups": [
], "properties": [
{
"name": "feign.hystrix.enabled",
"type": "java.lang.Boolean",
"description": "If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.",
"defaultValue": "false"
},
{
"name": "feign.httpclient.enabled",
"type": "java.lang.Boolean",
"description": "Enables the use of the Apache HTTP Client by Feign.",
"defaultValue": "true"
},
{
"name": "feign.okhttp.enabled",
"type": "java.lang.Boolean",
"description": "Enables the use of the OK HTTP Client by Feign.",
"defaultValue": "false"
},
{
"name": "feign.compression.response.enabled",
"type": "java.lang.Boolean",
"description": "Enables the response from Feign to be compressed.",
"defaultValue": "false"
},
{
"name": "feign.compression.request.enabled",
"type": "java.lang.Boolean",
"description": "Enables the request sent by Feign to be compressed.",
"defaultValue": "false"
}
]}
{
"groups": [
],
"properties": [
{
"name": "feign.hystrix.enabled",
"type": "java.lang.Boolean",
"description": "If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.",
"defaultValue": "false"
},
{
"name": "feign.httpclient.enabled",
"type": "java.lang.Boolean",
"description": "Enables the use of the Apache HTTP Client by Feign.",
"defaultValue": "true"
},
{
"name": "feign.okhttp.enabled",
"type": "java.lang.Boolean",
"description": "Enables the use of the OK HTTP Client by Feign.",
"defaultValue": "false"
},
{
"name": "feign.compression.response.enabled",
"type": "java.lang.Boolean",
"description": "Enables the response from Feign to be compressed.",
"defaultValue": "false"
},
{
"name": "feign.compression.request.enabled",
"type": "java.lang.Boolean",
"description": "Enables the request sent by Feign to be compressed.",
"defaultValue": "false"
}
]
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,13 +12,20 @@
* 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.Contract;
import feign.Feign;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.optionals.OptionalDecoder;
import feign.slf4j.Slf4jLogger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@@ -30,14 +37,6 @@ import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.optionals.OptionalDecoder;
import feign.slf4j.Slf4jLogger;
/**
* @author Spencer Gibb
*/
@@ -51,8 +50,7 @@ public class EnableFeignClientsTests {
@Test
public void decoderDefaultCorrect() {
OptionalDecoder.class
.cast(this.feignContext.getInstance("foo", Decoder.class));
OptionalDecoder.class.cast(this.feignContext.getInstance("foo", Decoder.class));
}
@Test
@@ -81,6 +79,7 @@ public class EnableFeignClientsTests {
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
FeignAutoConfiguration.class })
protected static class PlainConfiguration {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -25,16 +25,18 @@ import java.util.Collections;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.cloud.openfeign.testclients.TestClient;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Sven Döring
*/
@@ -65,14 +67,14 @@ public class FeignClientBuilderTests {
fieldName);
ReflectionUtils.makeAccessible(field);
final Object value = ReflectionUtils.getField(field, factoryBean);
Assert.assertEquals("Expected value for the field '" + fieldName + "':",
expectedValue, value);
assertThat(value).as("Expected value for the field '" + fieldName + "':")
.isEqualTo(expectedValue);
}
@Before
public void setUp() {
this.applicationContext = Mockito.mock(ApplicationContext.class);
this.feignClientBuilder = new FeignClientBuilder(applicationContext);
this.feignClientBuilder = new FeignClientBuilder(this.applicationContext);
}
@Test
@@ -91,18 +93,18 @@ public class FeignClientBuilderTests {
// on this builder class.
// (2) Or a new field was added and the builder class has to be extended with this
// new field.
Assert.assertThat(methodNames, Matchers.contains("contextId", "decode404", "fallback",
"fallbackFactory", "name", "path", "url"));
assertThat(methodNames).containsExactly("contextId", "decode404", "fallback",
"fallbackFactory", "name", "path", "url");
}
@Test
public void forType_preinitializedBuilder() {
// when:
final FeignClientBuilder.Builder builder = feignClientBuilder
final FeignClientBuilder.Builder builder = this.feignClientBuilder
.forType(FeignClientBuilderTests.class, "TestClient");
// then:
assertFactoryBeanField(builder, "applicationContext", applicationContext);
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class);
assertFactoryBeanField(builder, "name", "TestClient");
assertFactoryBeanField(builder, "contextId", "TestClient");
@@ -123,13 +125,13 @@ public class FeignClientBuilderTests {
@Test
public void forType_allFieldsSetOnBuilder() {
// when:
final FeignClientBuilder.Builder builder = feignClientBuilder
final FeignClientBuilder.Builder builder = this.feignClientBuilder
.forType(FeignClientBuilderTests.class, "TestClient").decode404(true)
.fallback(Object.class).fallbackFactory(Object.class).path("Path/")
.url("Url/");
// then:
assertFactoryBeanField(builder, "applicationContext", applicationContext);
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class);
assertFactoryBeanField(builder, "name", "TestClient");
@@ -144,16 +146,17 @@ public class FeignClientBuilderTests {
@Test
public void forType_build() {
// given:
Mockito.when(applicationContext.getBean(FeignContext.class))
Mockito.when(this.applicationContext.getBean(FeignContext.class))
.thenThrow(new ClosedFileSystemException()); // throw an unusual exception
// in the
// FeignClientFactoryBean
final FeignClientBuilder.Builder builder = feignClientBuilder
final FeignClientBuilder.Builder builder = this.feignClientBuilder
.forType(TestClient.class, "TestClient");
// expect: 'the build will fail right after calling build() with the mocked
// unusual exception'
thrown.expect(Matchers.isA(ClosedFileSystemException.class));
this.thrown.expect(Matchers.isA(ClosedFileSystemException.class));
builder.build();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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,16 +16,14 @@
package org.springframework.cloud.openfeign;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
@@ -42,33 +40,43 @@ public class FeignClientFactoryTests {
getSpec("bar", BarConfig.class)));
Foo foo = context.getInstance("foo", Foo.class);
assertThat("foo was null", foo, is(notNullValue()));
assertThat(foo).as("foo was null").isNotNull();
Bar bar = context.getInstance("bar", Bar.class);
assertThat("bar was null", bar, is(notNullValue()));
assertThat(bar).as("bar was null").isNotNull();
Bar foobar = context.getInstance("foo", Bar.class);
assertThat("bar was not null", foobar, is(nullValue()));
assertThat(foobar).as("bar was not null").isNull();
}
private FeignClientSpecification getSpec(String name, Class<?> configClass) {
return new FeignClientSpecification(name, new Class[]{configClass});
return new FeignClientSpecification(name, new Class[] { configClass });
}
static class FooConfig {
@Bean
Foo foo() {
return new Foo();
}
}
static class Foo{}
static class Foo {
}
static class BarConfig {
@Bean
Bar bar() {
return new Bar();
}
}
static class Bar{}
static class Bar {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,31 +12,10 @@
* 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 org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
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.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import feign.Contract;
import feign.Feign;
import feign.Logger;
@@ -52,6 +31,24 @@ import feign.codec.ErrorDecoder;
import feign.hystrix.HystrixFeign;
import feign.optionals.OptionalDecoder;
import feign.slf4j.Slf4jLogger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
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.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
@@ -72,8 +69,8 @@ public class FeignClientOverrideDefaultsTests {
@Test
public void clientsAvailable() {
assertNotNull(this.foo);
assertNotNull(this.bar);
assertThat(this.foo).isNotNull();
assertThat(this.bar).isNotNull();
}
@Test
@@ -102,45 +99,62 @@ public class FeignClientOverrideDefaultsTests {
@Test
public void overrideLoggerLevel() {
assertNull(this.context.getInstance("foo", Logger.Level.class));
assertEquals(Logger.Level.HEADERS,
this.context.getInstance("bar", Logger.Level.class));
assertThat(this.context.getInstance("foo", Logger.Level.class)).isNull();
assertThat(this.context.getInstance("bar", Logger.Level.class))
.isEqualTo(Logger.Level.HEADERS);
}
@Test
public void overrideRetryer() {
assertEquals(Retryer.NEVER_RETRY, this.context.getInstance("foo", Retryer.class));
assertThat(this.context.getInstance("foo", Retryer.class))
.isEqualTo(Retryer.NEVER_RETRY);
Retryer.Default.class.cast(this.context.getInstance("bar", Retryer.class));
}
@Test
public void overrideErrorDecoder() {
assertNull(this.context.getInstance("foo", ErrorDecoder.class));
assertThat(this.context.getInstance("foo", ErrorDecoder.class)).isNull();
ErrorDecoder.Default.class
.cast(this.context.getInstance("bar", ErrorDecoder.class));
}
@Test
public void overrideBuilder() {
HystrixFeign.Builder.class.cast(this.context.getInstance("foo", Feign.Builder.class));
Feign.Builder.class
.cast(this.context.getInstance("bar", Feign.Builder.class));
HystrixFeign.Builder.class
.cast(this.context.getInstance("foo", Feign.Builder.class));
Feign.Builder.class.cast(this.context.getInstance("bar", Feign.Builder.class));
}
@Test
public void overrideRequestOptions() {
assertNull(this.context.getInstance("foo", Request.Options.class));
assertThat(this.context.getInstance("foo", Request.Options.class)).isNull();
Request.Options options = this.context.getInstance("bar", Request.Options.class);
assertEquals(1, options.connectTimeoutMillis());
assertEquals(1, options.readTimeoutMillis());
assertThat(options.connectTimeoutMillis()).isEqualTo(1);
assertThat(options.readTimeoutMillis()).isEqualTo(1);
}
@Test
public void addRequestInterceptor() {
assertEquals(1,
this.context.getInstances("foo", RequestInterceptor.class).size());
assertEquals(2,
this.context.getInstances("bar", RequestInterceptor.class).size());
assertThat(this.context.getInstances("foo", RequestInterceptor.class).size())
.isEqualTo(1);
assertThat(this.context.getInstances("bar", RequestInterceptor.class).size())
.isEqualTo(2);
}
@FeignClient(name = "foo", url = "http://foo", configuration = FooConfiguration.class)
interface FooClient {
@RequestLine("GET /")
String get();
}
@FeignClient(name = "bar", url = "http://bar", configuration = BarConfiguration.class)
interface BarClient {
@RequestMapping(value = "/", method = RequestMethod.GET)
String get();
}
@Configuration
@@ -148,6 +162,7 @@ public class FeignClientOverrideDefaultsTests {
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
FeignAutoConfiguration.class })
protected static class TestConfiguration {
@Bean
RequestInterceptor defaultRequestInterceptor() {
return new RequestInterceptor() {
@@ -156,16 +171,11 @@ public class FeignClientOverrideDefaultsTests {
}
};
}
}
@FeignClient(name = "foo", url = "http://foo", configuration = FooConfiguration.class)
interface FooClient {
@RequestLine("GET /")
String get();
}
public static class FooConfiguration {
@Bean
public Decoder feignDecoder() {
return new Decoder.Default();
@@ -190,15 +200,11 @@ public class FeignClientOverrideDefaultsTests {
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
}
@FeignClient(name = "bar", url = "http://bar", configuration = BarConfiguration.class)
interface BarClient {
@RequestMapping(value = "/", method = RequestMethod.GET)
String get();
}
public static class BarConfiguration {
@Bean
Logger.Level feignLevel() {
return Logger.Level.HEADERS;
@@ -223,5 +229,7 @@ public class FeignClientOverrideDefaultsTests {
RequestInterceptor feignRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "pass");
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -37,7 +37,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
@@ -52,14 +51,15 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Eko Kurniawan Khannedy
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = RANDOM_PORT)
@TestPropertySource("classpath:feign-properties.properties")
@DirtiesContext
public class FeignClientUsingPropertiesTests {
@@ -80,38 +80,41 @@ public class FeignClientUsingPropertiesTests {
private FeignClientFactoryBean formFactoryBean;
public FeignClientUsingPropertiesTests() {
fooFactoryBean = new FeignClientFactoryBean();
fooFactoryBean.setContextId("foo");
fooFactoryBean.setType(FeignClientFactoryBean.class);
this.fooFactoryBean = new FeignClientFactoryBean();
this.fooFactoryBean.setContextId("foo");
this.fooFactoryBean.setType(FeignClientFactoryBean.class);
barFactoryBean = new FeignClientFactoryBean();
barFactoryBean.setContextId("bar");
barFactoryBean.setType(FeignClientFactoryBean.class);
this.barFactoryBean = new FeignClientFactoryBean();
this.barFactoryBean.setContextId("bar");
this.barFactoryBean.setType(FeignClientFactoryBean.class);
formFactoryBean = new FeignClientFactoryBean();
formFactoryBean.setContextId("form");
formFactoryBean.setType(FeignClientFactoryBean.class);
this.formFactoryBean = new FeignClientFactoryBean();
this.formFactoryBean.setContextId("form");
this.formFactoryBean.setType(FeignClientFactoryBean.class);
}
public FooClient fooClient() {
fooFactoryBean.setApplicationContext(applicationContext);
return fooFactoryBean.feign(context).target(FooClient.class, "http://localhost:" + this.port);
this.fooFactoryBean.setApplicationContext(this.applicationContext);
return this.fooFactoryBean.feign(this.context).target(FooClient.class,
"http://localhost:" + this.port);
}
public BarClient barClient() {
barFactoryBean.setApplicationContext(applicationContext);
return barFactoryBean.feign(context).target(BarClient.class, "http://localhost:" + this.port);
this.barFactoryBean.setApplicationContext(this.applicationContext);
return this.barFactoryBean.feign(this.context).target(BarClient.class,
"http://localhost:" + this.port);
}
public FormClient formClient() {
formFactoryBean.setApplicationContext(applicationContext);
return formFactoryBean.feign(context).target(FormClient.class, "http://localhost:" + this.port);
this.formFactoryBean.setApplicationContext(this.applicationContext);
return this.formFactoryBean.feign(this.context).target(FormClient.class,
"http://localhost:" + this.port);
}
@Test
public void testFoo() {
String response = fooClient().foo();
assertEquals("OK", response);
assertThat(response).isEqualTo("OK");
}
@Test(expected = RetryableException.class)
@@ -124,25 +127,26 @@ public class FeignClientUsingPropertiesTests {
public void testForm() {
Map<String, String> request = Collections.singletonMap("form", "Data");
String response = formClient().form(request);
assertEquals("Data", response);
assertThat(response).isEqualTo("Data");
}
protected interface FooClient {
@RequestMapping(method = RequestMethod.GET, value = "/foo")
String foo();
}
protected interface BarClient {
@RequestMapping(method = RequestMethod.GET, value = "/bar")
String bar();
}
protected interface FormClient {
@RequestMapping(value = "/form", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@RequestMapping(value = "/form", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
String form(Map<String, String> form);
}
@@ -155,10 +159,11 @@ public class FeignClientUsingPropertiesTests {
@RequestMapping(method = RequestMethod.GET, value = "/foo")
public String foo(HttpServletRequest request) throws IllegalAccessException {
if ("Foo".equals(request.getHeader("Foo")) &&
"Bar".equals(request.getHeader("Bar"))) {
if ("Foo".equals(request.getHeader("Foo"))
&& "Bar".equals(request.getHeader("Bar"))) {
return "OK";
} else {
}
else {
throw new IllegalAccessException("It should has Foo and Bar header");
}
}
@@ -169,8 +174,7 @@ public class FeignClientUsingPropertiesTests {
return "OK";
}
@RequestMapping(value = "/form", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@RequestMapping(value = "/form", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String form(HttpServletRequest request) {
return request.getParameter("form");
}
@@ -178,17 +182,21 @@ public class FeignClientUsingPropertiesTests {
}
public static class FooRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("Foo", "Foo");
}
}
public static class BarRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("Bar", "Bar");
}
}
public static class NoRetryer implements Retryer {
@@ -202,24 +210,29 @@ public class FeignClientUsingPropertiesTests {
public Retryer clone() {
return this;
}
}
public static class DefaultErrorDecoder extends ErrorDecoder.Default {
}
public static class FormEncoder implements Encoder {
@Override
public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException {
public void encode(Object o, Type type, RequestTemplate requestTemplate)
throws EncodeException {
Map<String, String> form = (Map<String, String>) o;
StringBuilder builder = new StringBuilder();
form.forEach((key, value) -> {
builder.append(key + "=" + value + "&");
});
requestTemplate.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
requestTemplate.header(HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_FORM_URLENCODED_VALUE);
requestTemplate.body(Request.Body.bodyTemplate(builder.toString(), UTF_8));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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;
@@ -20,6 +19,7 @@ package org.springframework.cloud.openfeign;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
@@ -27,8 +27,7 @@ import org.springframework.mock.env.MockEnvironment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
@@ -41,7 +40,6 @@ public class FeignClientsRegistrarTests {
testGetName("http://bad_hostname");
}
@Test(expected = IllegalStateException.class)
public void badNameHttpsPrefix() {
testGetName("https://bad_hostname");
@@ -60,19 +58,19 @@ public class FeignClientsRegistrarTests {
@Test
public void goodName() {
String name = testGetName("good-name");
assertThat("name was wrong", name, is("good-name"));
assertThat(name).as("name was wrong").isEqualTo("good-name");
}
@Test
public void goodNameHttpPrefix() {
String name = testGetName("http://good-name");
assertThat("name was wrong", name, is("http://good-name"));
assertThat(name).as("name was wrong").isEqualTo("http://good-name");
}
@Test
public void goodNameHttpsPrefix() {
String name = testGetName("https://goodname");
assertThat("name was wrong", name, is("https://goodname"));
assertThat(name).as("name was wrong").isEqualTo("https://goodname");
}
private String testGetName(String name) {
@@ -81,7 +79,6 @@ public class FeignClientsRegistrarTests {
return registrar.getName(Collections.<String, Object>singletonMap("name", name));
}
@Test(expected = IllegalArgumentException.class)
public void testFallback() {
new AnnotationConfigApplicationContext(FallbackTestConfig.class);
@@ -92,30 +89,35 @@ public class FeignClientsRegistrarTests {
new AnnotationConfigApplicationContext(FallbackFactoryTestConfig.class);
}
@Configuration
@EnableAutoConfiguration
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class})
protected static class FallbackTestConfig {
}
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class)
protected interface FallbackClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String fallbackTest();
}
@Configuration
@EnableAutoConfiguration
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackFactoryClient.class})
protected static class FallbackFactoryTestConfig {
}
@FeignClient(name = "fallbackFactoryTestClient", url = "http://localhost:8081/", fallbackFactory = FallbackFactoryClient.class)
protected interface FallbackFactoryClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String fallbackFactoryTest();
}
@Configuration
@EnableAutoConfiguration
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class })
protected static class FallbackTestConfig {
}
@Configuration
@EnableAutoConfiguration
@EnableFeignClients(clients = {
FeignClientsRegistrarTests.FallbackFactoryClient.class })
protected static class FallbackFactoryTestConfig {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,15 +12,18 @@
* 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.util.Map;
import feign.Client;
import feign.RequestInterceptor;
import feign.httpclient.ApacheHttpClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -35,10 +38,6 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.Client;
import feign.RequestInterceptor;
import feign.httpclient.ApacheHttpClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -90,7 +89,9 @@ public class FeignCompressionTests {
* a bean here of type ApacheHttpClient so that the configuration will be
* loaded correctly.
*/
return (ApacheHttpClient) client;
return (ApacheHttpClient) this.client;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,14 +12,16 @@
* 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.Field;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.http.config.Lookup;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
@@ -28,6 +30,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
@@ -36,8 +39,7 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.ReflectionUtils;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
@@ -50,8 +52,8 @@ public class FeignHttpClientConfigurationTests {
@Before
public void setUp() {
context = new SpringApplicationBuilder()
.properties("debug=true","feign.httpclient.disableSslValidation=true")
this.context = new SpringApplicationBuilder()
.properties("debug=true", "feign.httpclient.disableSslValidation=true")
.web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
.run();
@@ -59,29 +61,38 @@ public class FeignHttpClientConfigurationTests {
@After
public void tearDown() {
if(context != null) {
context.close();
if (this.context != null) {
this.context.close();
}
}
@Test
public void disableSslTest() throws Exception {
HttpClientConnectionManager connectionManager = context.getBean(HttpClientConnectionManager.class);
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager);
assertNotNull(socketFactoryRegistry.lookup("https"));
assertNull(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers());
HttpClientConnectionManager connectionManager = this.context
.getBean(HttpClientConnectionManager.class);
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(
connectionManager);
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers())
.isNull();
}
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(HttpClientConnectionManager connectionManager) {
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator)this.getField(connectionManager, "connectionOperator");
return (Lookup)this.getField(connectionOperator, "socketFactoryRegistry");
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
HttpClientConnectionManager connectionManager) {
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this
.getField(connectionManager, "connectionOperator");
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
}
private X509TrustManager getX509TrustManager(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory)socketFactoryRegistry.lookup("https");
SSLSocketFactory sslSocketFactory = (SSLSocketFactory)this.getField(connectionSocketFactory, "socketfactory");
SSLContextSpi sslContext = (SSLContextSpi)this.getField(sslSocketFactory, "context");
return (X509TrustManager)this.getField(sslContext, "trustManager");
private X509TrustManager getX509TrustManager(
Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry
.lookup("https");
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this
.getField(connectionSocketFactory, "socketfactory");
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory,
"context");
return (X509TrustManager) this.getField(sslContext, "trustManager");
}
protected <T> Object getField(Object target, String name) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,28 +12,25 @@
* 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 static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.lang.reflect.Field;
import java.util.Objects;
import feign.Client;
import feign.Feign;
import feign.Target;
import feign.httpclient.ApacheHttpClient;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -46,16 +43,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import feign.Client;
import feign.Feign;
import feign.Target;
import feign.httpclient.ApacheHttpClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
/**
* @author Spencer Gibb
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class, webEnvironment = WebEnvironment.DEFINED_PORT, value = {
@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class, webEnvironment = DEFINED_PORT, value = {
"spring.application.name=feignclienturltest", "feign.hystrix.enabled=false",
"feign.okhttp.enabled=false" })
@DirtiesContext
@@ -63,6 +58,15 @@ public class FeignHttpClientUrlTests {
static int port;
@Autowired
BeanUrlClientNoProtocol beanClientNoProtocol;
@Autowired
private UrlClient urlClient;
@Autowired
private BeanUrlClient beanClient;
@BeforeClass
public static void beforeClass() {
port = SocketUtils.findAvailableTcpPort();
@@ -74,37 +78,61 @@ public class FeignHttpClientUrlTests {
System.clearProperty("server.port");
}
@Autowired
private UrlClient urlClient;
@Test
public void testUrlHttpClient() {
assertThat(this.urlClient).as("UrlClient was null").isNotNull();
Hello hello = this.urlClient.getHello();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello("hello world 1"));
}
@Autowired
private BeanUrlClient beanClient;
@Test
public void testBeanUrl() {
Hello hello = this.beanClient.getHello();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello("hello world 1"));
}
@Autowired BeanUrlClientNoProtocol beanClientNoProtocol;
@Test
public void testBeanUrlNoProtocol() {
Hello hello = this.beanClientNoProtocol.getHello();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello("hello world 1"));
}
// this tests that
@FeignClient(name = "localappurl", url = "http://localhost:${server.port}/")
protected interface UrlClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
@FeignClient(name = "beanappurl", url = "#{SERVER_URL}path")
protected interface BeanUrlClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
@FeignClient(name = "beanappurlnoprotocol", url = "#{SERVER_URL_NO_PROTOCOL}path")
protected interface BeanUrlClientNoProtocol {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
@Configuration
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class, BeanUrlClientNoProtocol.class })
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class,
BeanUrlClientNoProtocol.class })
@Import(NoSecurityConfiguration.class)
protected static class TestConfig {
@@ -118,12 +146,12 @@ public class FeignHttpClientUrlTests {
return getHello();
}
@Bean(name="SERVER_URL")
@Bean(name = "SERVER_URL")
public String serverUrl() {
return "http://localhost:" + port + "/";
}
@Bean(name="SERVER_URL_NO_PROTOCOL")
@Bean(name = "SERVER_URL_NO_PROTOCOL")
public String serverUrlNoProtocol() {
return "localhost:" + port + "/";
}
@@ -139,8 +167,8 @@ public class FeignHttpClientUrlTests {
ReflectionUtils.makeAccessible(field);
Client client = (Client) ReflectionUtils.getField(field, feign);
if (target.name().equals("localappurl")) {
assertThat("client was wrong type", client,
is(instanceOf(ApacheHttpClient.class)));
assertThat(client).isInstanceOf(ApacheHttpClient.class)
.as("client was wrong type");
}
return feign.target(target);
}
@@ -149,29 +177,8 @@ public class FeignHttpClientUrlTests {
}
@Test
public void testUrlHttpClient() {
assertNotNull("UrlClient was null", this.urlClient);
Hello hello = this.urlClient.getHello();
assertNotNull("hello was null", hello);
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
}
@Test
public void testBeanUrl() {
Hello hello = this.beanClient.getHello();
assertNotNull("hello was null", hello);
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
}
@Test
public void testBeanUrlNoProtocol() {
Hello hello = this.beanClientNoProtocol.getHello();
assertNotNull("hello was null", hello);
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
}
public static class Hello {
private String message;
public Hello() {
@@ -182,7 +189,7 @@ public class FeignHttpClientUrlTests {
}
public String getMessage() {
return message;
return this.message;
}
public void setMessage(String message) {
@@ -191,15 +198,21 @@ public class FeignHttpClientUrlTests {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Hello that = (Hello) o;
return Objects.equals(message, that.message);
return Objects.equals(this.message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(message);
return Objects.hash(this.message);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2019 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,9 +16,8 @@
package org.springframework.cloud.openfeign;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import feign.Logger;
import feign.slf4j.Slf4jLogger;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -26,8 +25,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import feign.Logger;
import feign.slf4j.Slf4jLogger;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Venil Noronha
@@ -36,12 +34,38 @@ public class FeignLoggerFactoryTests {
@Test
public void testDefaultLogger() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration1.class);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SampleConfiguration1.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertNotNull(loggerFactory);
assertThat(loggerFactory).isNotNull();
Logger logger = loggerFactory.create(Object.class);
assertNotNull(logger);
assertTrue(logger instanceof Slf4jLogger);
assertThat(logger).isNotNull();
assertThat(logger instanceof Slf4jLogger).isTrue();
context.close();
}
@Test
public void testCustomLogger() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SampleConfiguration2.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertThat(loggerFactory).isNotNull();
Logger logger = loggerFactory.create(Object.class);
assertThat(logger).isNotNull();
assertThat(logger instanceof LoggerImpl1).isTrue();
context.close();
}
@Test
public void testCustomLoggerFactory() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
SampleConfiguration3.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertThat(loggerFactory).isNotNull();
assertThat(loggerFactory instanceof LoggerFactoryImpl).isTrue();
Logger logger = loggerFactory.create(Object.class);
assertThat(logger).isNotNull();
assertThat(logger instanceof LoggerImpl2).isTrue();
context.close();
}
@@ -51,17 +75,6 @@ public class FeignLoggerFactoryTests {
}
@Test
public void testCustomLogger() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration2.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertNotNull(loggerFactory);
Logger logger = loggerFactory.create(Object.class);
assertNotNull(logger);
assertTrue(logger instanceof LoggerImpl1);
context.close();
}
@Configuration
@Import(FeignClientsConfiguration.class)
protected static class SampleConfiguration2 {
@@ -82,18 +95,6 @@ public class FeignLoggerFactoryTests {
}
@Test
public void testCustomLoggerFactory() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration3.class);
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
assertNotNull(loggerFactory);
assertTrue(loggerFactory instanceof LoggerFactoryImpl);
Logger logger = loggerFactory.create(Object.class);
assertNotNull(logger);
assertTrue(logger instanceof LoggerImpl2);
context.close();
}
@Configuration
@Import(FeignClientsConfiguration.class)
protected static class SampleConfiguration3 {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,19 +12,20 @@
* 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 okhttp3.OkHttpClient;
import java.lang.reflect.Field;
import javax.net.ssl.HostnameVerifier;
import okhttp3.OkHttpClient;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
@@ -34,6 +35,8 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@@ -45,23 +48,29 @@ public class FeignOkHttpConfigurationTests {
@Before
public void setUp() {
context = new SpringApplicationBuilder().properties("debug=true","feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false").web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
this.context = new SpringApplicationBuilder()
.properties("debug=true", "feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false")
.web(WebApplicationType.NONE)
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
.run();
}
@After
public void tearDown() {
if(context != null) {
context.close();
if (this.context != null) {
this.context.close();
}
}
@Test
public void disableSslTest() throws Exception {
OkHttpClient httpClient = context.getBean(OkHttpClient.class);
HostnameVerifier hostnameVerifier = (HostnameVerifier)this.getField(httpClient, "hostnameVerifier");
Assert.assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier));
OkHttpClient httpClient = this.context.getBean(OkHttpClient.class);
HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient,
"hostnameVerifier");
assertThat(
OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier))
.isTrue();
}
protected <T> Object getField(Object target, String name) {
@@ -70,4 +79,5 @@ public class FeignOkHttpConfigurationTests {
Object value = ReflectionUtils.getField(field, target);
return value;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -23,6 +23,7 @@ import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -40,10 +41,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
@@ -72,66 +70,73 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
public TestClient testClient(boolean decode404) {
setType(this.getClass());
setDecode404(decode404);
return feign(context).target(TestClient.class, "http://localhost:" + this.port);
return feign(this.context).target(TestClient.class,
"http://localhost:" + this.port);
}
@Test
public void testResponseEntity() {
ResponseEntity<Hello> response = testClient().getHelloResponse();
assertNotNull("response was null", response);
assertEquals("wrong status code", HttpStatus.OK, response.getStatusCode());
assertThat(response).as("response was null").isNotNull();
assertThat(response.getStatusCode()).as("wrong status code")
.isEqualTo(HttpStatus.OK);
Hello hello = response.getBody();
assertNotNull("hello was null", hello);
assertEquals("first hello didn't match", new Hello("hello world via response"),
hello);
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello("hello world via response"));
}
@Test
public void testSimpleType() {
Hello hello = testClient().getHello();
assertNotNull("hello was null", hello);
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match")
.isEqualTo(new Hello("hello world 1"));
}
@Test
public void testUserParameterizedTypeDecode() {
List<Hello> hellos = testClient().getHellos();
assertNotNull("hellos was null", hellos);
assertEquals("hellos was not the right size", 2, hellos.size());
assertEquals("first hello didn't match", new Hello("hello world 1"),
hellos.get(0));
assertThat(hellos).as("hellos was null").isNotNull();
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
assertThat(hellos.get(0)).as("first hello didn't match")
.isEqualTo(new Hello("hello world 1"));
}
@Test
public void testSimpleParameterizedTypeDecode() {
List<String> hellos = testClient().getHelloStrings();
assertNotNull("hellos was null", hellos);
assertEquals("hellos was not the right size", 2, hellos.size());
assertEquals("first hello didn't match", "hello world 1", hellos.get(0));
assertThat(hellos).as("hellos was null").isNotNull();
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
assertThat(hellos.get(0)).as("first hello didn't match")
.isEqualTo("hello world 1");
}
@Test
@SuppressWarnings("unchecked")
public void testWildcardTypeDecode() {
ResponseEntity<?> wildcard = testClient().getWildcard();
assertNotNull("wildcard was null", wildcard);
assertEquals("wrong status code", HttpStatus.OK, wildcard.getStatusCode());
assertThat(wildcard).as("wildcard was null").isNotNull();
assertThat(wildcard.getStatusCode()).as("wrong status code")
.isEqualTo(HttpStatus.OK);
Object wildcardBody = wildcard.getBody();
assertNotNull("wildcardBody was null", wildcardBody);
assertTrue("wildcard not an instance of Map", wildcardBody instanceof Map);
assertThat(wildcardBody).as("wildcardBody was null").isNotNull();
assertThat(wildcardBody instanceof Map).as("wildcard not an instance of Map")
.isTrue();
Map<String, String> hello = (Map<String, String>) wildcardBody;
assertEquals("first hello didn't match", "wildcard", hello.get("message"));
assertThat(hello.get("message")).as("first hello didn't match")
.isEqualTo("wildcard");
}
@Test
public void testResponseEntityVoid() {
ResponseEntity<Void> response = testClient().getHelloVoid();
assertNotNull("response was null", response);
assertThat(response).as("response was null").isNotNull();
List<String> headerVals = response.getHeaders().get("X-test-header");
assertNotNull("headerVals was null", headerVals);
assertEquals("headerVals size was wrong", 1, headerVals.size());
assertThat(headerVals).as("headerVals was null").isNotNull();
assertThat(headerVals.size()).as("headerVals size was wrong").isEqualTo(1);
String header = headerVals.get(0);
assertEquals("header was wrong", "myval", header);
assertThat(header).as("header was wrong").isEqualTo("myval");
}
@Test(expected = RuntimeException.class)
@@ -142,43 +147,12 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
@Test
public void testDecodes404() {
final ResponseEntity<String> response = testClient(true).getNotFound();
assertNotNull("response was null", response);
assertNull("response body was not null", response.getBody());
}
public static class Hello {
private String message;
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return 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(message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(message);
}
assertThat(response).as("response was null").isNotNull();
assertThat(response.getBody()).as("response body was not null").isNull();
}
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/helloresponse")
ResponseEntity<Hello> getHelloResponse();
@@ -199,6 +173,45 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
@GetMapping("/helloWildcard")
ResponseEntity<?> getWildcard();
}
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
@@ -247,7 +260,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
public ResponseEntity<?> getWildcard() {
return ResponseEntity.ok(new Hello("wildcard"));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -14,62 +14,64 @@
* limitations under the License.
*/
package org.springframework.cloud.openfeign;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.test.ClassPathExclusions;
import org.springframework.cloud.test.ModifiedClassPathRunner;
import org.springframework.context.ConfigurableApplicationContext;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.Is.is;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(ModifiedClassPathRunner.class)
@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"})
@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" })
public class SpringRetryDisabledTests {
private ConfigurableApplicationContext context;
@Before
public void setUp() {
context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.sources(RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class,
FeignRibbonClientAutoConfiguration.class).run();
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
.sources(RibbonAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
RibbonClientConfiguration.class,
FeignRibbonClientAutoConfiguration.class)
.run();
}
@After
public void tearDown() {
if(context != null) {
context.close();
if (this.context != null) {
this.context.close();
}
}
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class);
assertThat(lbFactorys.values(), hasSize(1));
FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo");
assertThat(lb, instanceOf(FeignLoadBalancer.class));
assertThat(lb, is(not(instanceOf(RetryableFeignLoadBalancer.class))));
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
assertThat(lbFactorys.values()).hasSize(1);
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
assertThat(lb).isInstanceOf(FeignLoadBalancer.class);
assertThat(lb).isNotInstanceOf(RetryableFeignLoadBalancer.class);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -17,46 +17,49 @@
package org.springframework.cloud.openfeign;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class})
@ContextConfiguration(classes = { RibbonAutoConfiguration.class,
RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class })
public class SpringRetryEnabledTests implements ApplicationContextAware {
private ApplicationContext context;
@Test
public void testLoadBalancedRetryFactoryBean() throws Exception {
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class);
assertThat(lbFactorys.values(), Matchers.hasSize(1));
FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo");
assertThat(lb, instanceOf(RetryableFeignLoadBalancer.class));
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
assertThat(lbFactorys.values()).hasSize(1);
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
assertThat(lb).isInstanceOf(RetryableFeignLoadBalancer.class);
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -23,6 +23,7 @@ import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
@@ -42,8 +43,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
@@ -73,6 +73,44 @@ public class FeignClientTests {
@Autowired
private TestClient buildByBuilder;
@Test
public void testAnnotations() {
Map<String, Object> beans = this.context
.getBeansWithAnnotation(FeignClient.class);
assertThat(beans.containsKey(TestClient.class.getName()))
.as("Wrong clients: " + beans).isTrue();
}
@Test
public void testClient() {
assertThat(this.testClient).as("testClient was null").isNotNull();
assertThat(this.extraClient).as("extraClient was null").isNotNull();
assertThat(Proxy.isProxyClass(this.testClient.getClass()))
.as("testClient is not a java Proxy").isTrue();
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
}
@Test
public void extraClient() {
assertThat(this.extraClient).as("extraClient was null").isNotNull();
assertThat(Proxy.isProxyClass(this.extraClient.getClass()))
.as("extraClient is not a java Proxy").isTrue();
InvocationHandler invocationHandler = Proxy
.getInvocationHandler(this.extraClient);
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
}
@Test
public void buildByBuilder() {
assertThat(this.buildByBuilder).as("buildByBuilder was null").isNotNull();
assertThat(Proxy.isProxyClass(this.buildByBuilder.getClass()))
.as("buildByBuilder is not a java Proxy").isTrue();
InvocationHandler invocationHandler = Proxy
.getInvocationHandler(this.buildByBuilder);
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
}
@Configuration
@EnableAutoConfiguration
@RestController
@@ -82,18 +120,18 @@ public class FeignClientTests {
@Bean("build-by-builder")
public TestClient buildByBuilder(final FeignClientBuilder feignClientBuilder) {
return feignClientBuilder
.forType(TestClient.class, "builderapp")
.build();
return feignClientBuilder.forType(TestClient.class, "builderapp").build();
}
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public Hello getHello() {
return new Hello("hello world 1");
}
}
public static class Hello {
private String message;
public Hello() {
@@ -104,7 +142,7 @@ public class FeignClientTests {
}
public String getMessage() {
return message;
return this.message;
}
public void setMessage(String message) {
@@ -113,56 +151,27 @@ public class FeignClientTests {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Hello that = (Hello) o;
return Objects.equals(message, that.message);
return Objects.equals(this.message, that.message);
}
@Override
public int hashCode() {
return message != null ? message.hashCode() : 0;
return this.message != null ? this.message.hashCode() : 0;
}
}
@Test
public void testAnnotations() {
Map<String, Object> beans = this.context
.getBeansWithAnnotation(FeignClient.class);
assertTrue("Wrong clients: " + beans,
beans.containsKey(TestClient.class.getName()));
}
@Test
public void testClient() {
assertNotNull("testClient was null", this.testClient);
assertNotNull("extraClient was null", this.extraClient);
assertTrue("testClient is not a java Proxy",
Proxy.isProxyClass(this.testClient.getClass()));
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
assertNotNull("invocationHandler was null", invocationHandler);
}
@Test
public void extraClient() {
assertNotNull("extraClient was null", this.extraClient);
assertTrue("extraClient is not a java Proxy",
Proxy.isProxyClass(this.extraClient.getClass()));
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.extraClient);
assertNotNull("invocationHandler was null", invocationHandler);
}
@Test
public void buildByBuilder() {
assertNotNull("buildByBuilder was null", this.buildByBuilder);
assertTrue("buildByBuilder is not a java Proxy",
Proxy.isProxyClass(this.buildByBuilder.getClass()));
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.buildByBuilder);
assertNotNull("invocationHandler was null", invocationHandler);
}
@Configuration
public static class TestDefaultFeignConfig {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -23,8 +23,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Primary
@FeignClient(value = "localapp")
@FeignClient("localapp")
public interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(value = "otherapp", qualifier = "uniquequalifier")
public interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -22,7 +22,6 @@ import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,11 +29,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -44,15 +42,15 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* Tests the response compression.
*
* @author Jakub Narloch
*/
@SpringBootTest(classes = FeignAcceptEncodingTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
@SpringBootTest(classes = FeignAcceptEncodingTests.Application.class, webEnvironment = RANDOM_PORT, value = {
"feign.compression.response.enabled=true" })
@RunWith(SpringRunner.class)
@DirtiesContext
@@ -68,10 +66,10 @@ public class FeignAcceptEncodingTests {
final ResponseEntity<List<Invoice>> invoices = this.invoiceClient.getInvoices();
// then
assertNotNull(invoices);
assertEquals(HttpStatus.OK, invoices.getStatusCode());
assertNotNull(invoices.getBody());
assertEquals(100, invoices.getBody().size());
assertThat(invoices).isNotNull();
assertThat(invoices.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(invoices.getBody()).isNotNull();
assertThat(invoices.getBody().size()).isEqualTo(100);
}
@@ -80,6 +78,7 @@ public class FeignAcceptEncodingTests {
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
@Import(NoSecurityConfiguration.class)
public static class Application {
}
@Configuration
@@ -95,5 +94,7 @@ public class FeignAcceptEncodingTests {
Collections.singletonList(new Server("localhost", this.port)));
return balancer;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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,23 +16,23 @@
package org.springframework.cloud.openfeign.encoding;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Collections;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -41,16 +41,15 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* Tests the response compression.
*
* @author Jakub Narloch
*/
@SpringBootTest(classes = FeignContentEncodingTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
@SpringBootTest(classes = FeignContentEncodingTests.Application.class, webEnvironment = RANDOM_PORT, value = {
"feign.compression.request.enabled=true",
"hystrix.command.default.execution.isolation.strategy=SEMAPHORE",
"ribbon.OkToRetryOnAllOperations=false" })
@@ -71,10 +70,10 @@ public class FeignContentEncodingTests {
.saveInvoices(invoices);
// then
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals(invoices.size(), response.getBody().size());
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().size()).isEqualTo(invoices.size());
}
@@ -83,6 +82,7 @@ public class FeignContentEncodingTests {
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
@Import(NoSecurityConfiguration.class)
public static class Application {
}
@Configuration
@@ -98,5 +98,7 @@ public class FeignContentEncodingTests {
Collections.singletonList(new Server("localhost", this.port)));
return balancer;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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,13 +16,13 @@
package org.springframework.cloud.openfeign.encoding;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
/**
* Utility class used for testing.
*
@@ -35,9 +35,11 @@ final class Invoices {
for (int ind = 0; ind < count; ind++) {
final Invoice invoice = new Invoice();
invoice.setTitle("Invoice " + (ind + 1));
invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000)));
invoice.setAmount(new BigDecimal(
String.format(Locale.US, "%.2f", Math.random() * 1000)));
invoices.add(invoice);
}
return invoices;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -38,4 +38,5 @@ public interface InvoiceClient {
@RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<List<Invoice>> saveInvoices(List<Invoice> invoices);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -30,7 +30,7 @@ public class Invoice {
private BigDecimal amount;
public String getTitle() {
return title;
return this.title;
}
public void setTitle(String title) {
@@ -38,10 +38,11 @@ public class Invoice {
}
public BigDecimal getAmount() {
return amount;
return this.amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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,6 +16,11 @@
package org.springframework.cloud.openfeign.encoding.app.resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -24,11 +29,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* An sample REST controller, that potentially returns large response - used for testing.
*
@@ -43,8 +43,7 @@ public class InvoiceResource {
return ResponseEntity.ok(createInvoiceList(100));
}
@RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
@RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<List<Invoice>> saveInvoices(@RequestBody List<Invoice> invoices) {
return ResponseEntity.ok(invoices);
@@ -55,9 +54,11 @@ public class InvoiceResource {
for (int ind = 0; ind < count; ind++) {
final Invoice invoice = new Invoice();
invoice.setTitle("Invoice " + (ind + 1));
invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000)));
invoice.setAmount(new BigDecimal(
String.format(Locale.US, "%.2f", Math.random() * 1000)));
invoices.add(invoice);
}
return invoices;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -39,17 +39,17 @@ import static feign.Request.HttpMethod.POST;
@ClassPathExclusions("protobuf-*.jar")
public class ProtobufNotInClasspathTest {
@Test
public void testEncodeWhenProtobufNotInClasspath() {
ObjectFactory<HttpMessageConverters> converters = new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(new StringHttpMessageConverter());
}
};
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.method(POST);
new SpringEncoder(converters).encode("a=b", String.class, requestTemplate);
}
@Test
public void testEncodeWhenProtobufNotInClasspath() {
ObjectFactory<HttpMessageConverters> converters = new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(new StringHttpMessageConverter());
}
};
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.method(POST);
new SpringEncoder(converters).encode("a=b", String.class, requestTemplate);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -35,7 +35,6 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
@@ -53,6 +52,8 @@ import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import static feign.Request.Body.encoded;
import static feign.Request.HttpMethod.POST;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* Test {@link SpringEncoder} with {@link ProtobufHttpMessageConverter}
@@ -62,87 +63,92 @@ import static feign.Request.HttpMethod.POST;
@RunWith(MockitoJUnitRunner.class)
public class ProtobufSpringEncoderTest {
@Mock
private HttpClient httpClient;
@Mock
private HttpClient httpClient;
// a protobuf object with some content
private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request.newBuilder()
.setId(1000000)
.setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台," +
"电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。")
.build();
// a protobuf object with some content
private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request
.newBuilder().setId(1000000).setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台,"
+ "电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。")
.build();
@Test
public void testProtobuf() throws IOException, URISyntaxException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(request, Request.class, requestTemplate);
HttpEntity entity = toApacheHttpEntity(requestTemplate);
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
@Test
public void testProtobuf() throws IOException, URISyntaxException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(this.request, Request.class, requestTemplate);
HttpEntity entity = toApacheHttpEntity(requestTemplate);
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
Assert.assertArrayEquals(bytes, request.toByteArray());
org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request.parseFrom(bytes);
Assert.assertEquals(request, copy);
}
assertThat(this.request.toByteArray()).isEqualTo(bytes);
org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request
.parseFrom(bytes);
assertThat(copy).isEqualTo(this.request);
}
@Test
public void testProtobufWithCharsetWillFail() throws IOException, URISyntaxException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(request, Request.class, requestTemplate);
// set a charset
requestTemplate
.body(encoded(requestTemplate.requestBody()
.asBytes(), StandardCharsets.UTF_8));
HttpEntity entity = toApacheHttpEntity(requestTemplate);
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
@Test
public void testProtobufWithCharsetWillFail() throws IOException, URISyntaxException {
// protobuf convert to request by feign and ProtobufHttpMessageConverter
RequestTemplate requestTemplate = newRequestTemplate();
newEncoder().encode(this.request, Request.class, requestTemplate);
// set a charset
requestTemplate.body(
encoded(requestTemplate.requestBody().asBytes(), StandardCharsets.UTF_8));
HttpEntity entity = toApacheHttpEntity(requestTemplate);
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
// http request-body is different with original protobuf body
Assert.assertNotEquals(bytes.length, request.toByteArray().length);
try {
org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request.parseFrom(bytes);
Assert.fail("Expected an InvalidProtocolBufferException to be thrown");
} catch (InvalidProtocolBufferException e) {
// success
}
}
// http request-body is different with original protobuf body
assertThat(this.request.toByteArray().length).isNotEqualTo(bytes.length);
try {
org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request
.parseFrom(bytes);
fail("Expected an InvalidProtocolBufferException to be thrown");
}
catch (InvalidProtocolBufferException e) {
// success
}
}
private SpringEncoder newEncoder() {
ObjectFactory<HttpMessageConverters> converters = new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(new ProtobufHttpMessageConverter());
}
};
return new SpringEncoder(converters);
}
private SpringEncoder newEncoder() {
ObjectFactory<HttpMessageConverters> converters = new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return new HttpMessageConverters(new ProtobufHttpMessageConverter());
}
};
return new SpringEncoder(converters);
}
private RequestTemplate newRequestTemplate() {
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.method(POST);
return requestTemplate;
}
private RequestTemplate newRequestTemplate() {
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.method(POST);
return requestTemplate;
}
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate) throws IOException, URISyntaxException {
final List<HttpUriRequest> request = new ArrayList<>(1);
BDDMockito.given(httpClient.execute(ArgumentMatchers.<HttpUriRequest>any()))
.will(new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
return new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("http", 1, 1), 200, null));
}
});
new ApacheHttpClient(httpClient).execute(requestTemplate.resolve(new HashMap<>())
.request(), new feign.Request.Options());
HttpUriRequest httpUriRequest = request.get(0);
return ((HttpEntityEnclosingRequestBase)httpUriRequest).getEntity();
}
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate)
throws IOException, URISyntaxException {
final List<HttpUriRequest> request = new ArrayList<>(1);
BDDMockito.given(this.httpClient.execute(ArgumentMatchers.<HttpUriRequest>any()))
.will(new Answer<HttpResponse>() {
@Override
public HttpResponse answer(InvocationOnMock invocationOnMock)
throws Throwable {
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
return new BasicHttpResponse(new BasicStatusLine(
new ProtocolVersion("http", 1, 1), 200, null));
}
});
new ApacheHttpClient(this.httpClient).execute(
requestTemplate.resolve(new HashMap<>()).request(),
new feign.Request.Options());
HttpUriRequest httpUriRequest = request.get(0);
return ((HttpEntityEnclosingRequestBase) httpUriRequest).getEntity();
}
private byte[] read(InputStream in, int length) throws IOException {
byte[] bytes = new byte[length];
in.read(bytes);
return bytes;
}
private byte[] read(InputStream in, int length) throws IOException {
byte[] bytes = new byte[length];
in.read(bytes);
return bytes;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2013-2019 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.
@@ -20,53 +20,48 @@
package org.springframework.cloud.openfeign.encoding.proto;
public final class ProtobufTest {
private ProtobufTest() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
static final com.google.protobuf.Descriptors.Descriptor
internal_static_Request_descriptor;
static final
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
internal_static_Request_fieldAccessorTable;
static final com.google.protobuf.Descriptors.Descriptor internal_static_Request_descriptor;
static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_Request_fieldAccessorTable;
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
String[] descriptorData = {
"\n\023protobuf_test.proto\"\"\n\007Request\022\n\n\002id\030\001" +
" \001(\005\022\013\n\003msg\030\002 \001(\tB\024\n\020feign.httpclientP\001b" +
"\006proto3"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
}, assigner);
internal_static_Request_descriptor =
getDescriptor().getMessageTypes().get(0);
internal_static_Request_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_Request_descriptor,
new String[] { "Id", "Msg", });
}
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
static {
String[] descriptorData = {
"\n\023protobuf_test.proto\"\"\n\007Request\022\n\n\002id\030\001"
+ " \001(\005\022\013\n\003msg\030\002 \001(\tB\024\n\020feign.httpclientP\001b"
+ "\006proto3" };
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(
descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {},
assigner);
internal_static_Request_descriptor = getDescriptor().getMessageTypes().get(0);
internal_static_Request_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_Request_descriptor, new String[] { "Id", "Msg", });
}
private ProtobufTest() {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
}
public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() {
return descriptor;
}
// @@protoc_insertion_point(outer_class_scope)
// @@protoc_insertion_point(outer_class_scope)
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 the original author or authors.
* Copyright 2013-2019 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.
@@ -20,21 +20,22 @@
package org.springframework.cloud.openfeign.encoding.proto;
public interface RequestOrBuilder extends
// @@protoc_insertion_point(interface_extends:Request)
com.google.protobuf.MessageOrBuilder {
// @@protoc_insertion_point(interface_extends:Request)
com.google.protobuf.MessageOrBuilder {
/**
* <code>int32 id = 1;</code>
*/
int getId();
/**
* <code>int32 id = 1;</code>
*/
int getId();
/**
* <code>string msg = 2;</code>
*/
String getMsg();
/**
* <code>string msg = 2;</code>
*/
com.google.protobuf.ByteString getMsgBytes();
/**
* <code>string msg = 2;</code>
*/
String getMsg();
/**
* <code>string msg = 2;</code>
*/
com.google.protobuf.ByteString
getMsgBytes();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -51,50 +51,27 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests that a secured web service returning values using a feign client properly access
* the security context from a hystrix command.
*
* @author Daniel Lavoie
*/
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "feign.hystrix.enabled=true"})
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"feign.hystrix.enabled=true" })
@ActiveProfiles("proxysecurity")
public class HystrixSecurityTests {
@Autowired
private CustomConcurrenyStrategy customConcurrenyStrategy;
@LocalServerPort
private String serverPort;
//TODO: move to constants in TestAutoConfiguration
// TODO: move to constants in TestAutoConfiguration
private String username = "user";
private String password = "password";
@Test
public void testSecurityConcurrencyStrategyInstalled() {
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
assertThat(concurrencyStrategy).isInstanceOf(SecurityContextConcurrencyStrategy.class);
}
@Test
public void testFeignHystrixSecurity() {
HttpHeaders headers = createBasicAuthHeader(username, password);
ResponseEntity<String> entity = new RestTemplate()
.exchange("http://localhost:" + serverPort + "/proxy-username",
HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody())
.as("Username should have been intercepted by feign interceptor.")
.isEqualTo(username);
assertThat(customConcurrenyStrategy.isHookCalled())
.as("Custom hook should have been called.")
.isTrue();
}
public static HttpHeaders createBasicAuthHeader(final String username,
final String password) {
return new HttpHeaders() {
@@ -109,10 +86,38 @@ public class HystrixSecurityTests {
};
}
@Test
public void testSecurityConcurrencyStrategyInstalled() {
HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance()
.getConcurrencyStrategy();
assertThat(concurrencyStrategy)
.isInstanceOf(SecurityContextConcurrencyStrategy.class);
}
@Test
public void testFeignHystrixSecurity() {
HttpHeaders headers = createBasicAuthHeader(this.username, this.password);
ResponseEntity<String> entity = new RestTemplate().exchange(
"http://localhost:" + this.serverPort + "/proxy-username", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody())
.as("Username should have been intercepted by feign interceptor.")
.isEqualTo(this.username);
assertThat(this.customConcurrenyStrategy.isHookCalled())
.as("Custom hook should have been called.").isTrue();
}
@SpringBootConfiguration
@Import(HystrixSecurityApplication.class)
@RibbonClient(name = "username", configuration = LocalRibbonClientConfiguration.class)
protected static class TestConfig { }
protected static class TestConfig {
}
protected static class LocalRibbonClientConfiguration {
@@ -125,4 +130,5 @@ public class HystrixSecurityTests {
}
}
}

View File

@@ -1,10 +1,27 @@
package org.springframework.cloud.openfeign.hystrix.security.app;
/*
* Copyright 2013-2019 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
*
* http://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.
*/
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
package org.springframework.cloud.openfeign.hystrix.security.app;
import java.util.concurrent.Callable;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy {
private boolean hookCalled;
@Override
@@ -15,6 +32,7 @@ public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy {
}
public boolean isHookCalled() {
return hookCalled;
return this.hookCalled;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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,11 +26,13 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/proxy-username")
public class ProxyUsernameController {
@Autowired
private UsernameClient usernameClient;
@RequestMapping
public String getUsername() {
return usernameClient.getUsername();
return this.usernameClient.getUsername();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -24,15 +24,17 @@ import org.springframework.security.core.context.SecurityContextHolder;
/**
* This interceptor should be called from an Hyxtrix command execution thread. It is
* access the SecurityContext and settings an http header from the authentication details.
*
*
* @author Daniel Lavoie
*/
public class TestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
if (SecurityContextHolder.getContext().getAuthentication() != null)
if (SecurityContextHolder.getContext().getAuthentication() != null) {
template.header("username",
SecurityContextHolder.getContext().getAuthentication().getName());
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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.
@@ -27,4 +27,5 @@ public interface UsernameClient {
@RequestMapping("/username")
String getUsername();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 the original author or authors.
* Copyright 2013-2019 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,8 +26,10 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/username")
public class UsernameController {
@RequestMapping
public String getUsername(@RequestHeader String username){
public String getUsername(@RequestHeader String username) {
return username;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -19,10 +19,10 @@ package org.springframework.cloud.openfeign.invalid;
import feign.Feign;
import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
@@ -37,7 +37,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.junit.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
@@ -53,6 +53,81 @@ public class FeignClientValidationTests {
new AnnotationConfigApplicationContext(NameAndValueConfiguration.class);
}
@Test
public void testServiceIdAndValue() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
NameAndServiceIdConfiguration.class);
assertThat(context.getBean(NameAndServiceIdConfiguration.Client.class))
.isNotNull();
context.close();
}
@Test
public void testDuplicatedClientNames() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.setAllowBeanDefinitionOverriding(false);
context.register(LoadBalancerAutoConfiguration.class,
RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class,
DuplicatedFeignClientNamesConfiguration.class);
context.refresh();
assertThat(
context.getBean(DuplicatedFeignClientNamesConfiguration.FooClient.class))
.isNotNull();
assertThat(
context.getBean(DuplicatedFeignClientNamesConfiguration.BarClient.class))
.isNotNull();
context.close();
}
@Test
public void testNotLegalHostname() {
this.expected.expectMessage("not legal hostname (foo_bar)");
new AnnotationConfigApplicationContext(BadHostnameConfiguration.class);
}
@Test
public void testMissingFallback() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MissingFallbackConfiguration.class)) {
this.expected.expectMessage("No fallback instance of type");
assertThat(context.getBean(MissingFallbackConfiguration.Client.class))
.isNotNull();
}
}
@Test
public void testWrongFallbackType() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
WrongFallbackTypeConfiguration.class)) {
this.expected.expectMessage("Incompatible fallback instance");
assertThat(context.getBean(WrongFallbackTypeConfiguration.Client.class))
.isNotNull();
}
}
@Test
public void testMissingFallbackFactory() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MissingFallbackFactoryConfiguration.class)) {
this.expected.expectMessage("No fallbackFactory instance of type");
assertThat(context.getBean(MissingFallbackFactoryConfiguration.Client.class))
.isNotNull();
}
}
@Test
public void testWrongFallbackFactoryType() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
WrongFallbackFactoryTypeConfiguration.class)) {
this.expected.expectMessage("Incompatible fallbackFactory instance");
assertThat(
context.getBean(WrongFallbackFactoryTypeConfiguration.Client.class))
.isNotNull();
}
}
@Configuration
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = NameAndValueConfiguration.Client.class)
@@ -60,75 +135,53 @@ public class FeignClientValidationTests {
@FeignClient(value = "foo", name = "bar")
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
}
@Test
public void testServiceIdAndValue() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
LoadBalancerAutoConfiguration.class,
RibbonAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
NameAndServiceIdConfiguration.class);
assertNotNull(context.getBean(NameAndServiceIdConfiguration.Client.class));
context.close();
}
@Configuration
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
@EnableFeignClients(clients = NameAndServiceIdConfiguration.Client.class)
protected static class NameAndServiceIdConfiguration {
@FeignClient(name = "bar", serviceId = "foo")
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
}
@Test
public void testDuplicatedClientNames() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.setAllowBeanDefinitionOverriding(false);
context.register(
LoadBalancerAutoConfiguration.class,
RibbonAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
DuplicatedFeignClientNamesConfiguration.class
);
context.refresh();
assertNotNull(context.getBean(DuplicatedFeignClientNamesConfiguration.FooClient.class));
assertNotNull(context.getBean(DuplicatedFeignClientNamesConfiguration.BarClient.class));
context.close();
}
}
@Configuration
@Import({FeignAutoConfiguration.class, HttpClientConfiguration.class})
@EnableFeignClients(clients = {DuplicatedFeignClientNamesConfiguration.FooClient.class,
DuplicatedFeignClientNamesConfiguration.BarClient.class})
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
@EnableFeignClients(clients = {
DuplicatedFeignClientNamesConfiguration.FooClient.class,
DuplicatedFeignClientNamesConfiguration.BarClient.class })
protected static class DuplicatedFeignClientNamesConfiguration {
@FeignClient(contextId = "foo", name = "bar")
interface FooClient {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
@FeignClient(name = "bar")
interface BarClient {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
}
@Test
public void testNotLegalHostname() {
this.expected.expectMessage("not legal hostname (foo_bar)");
new AnnotationConfigApplicationContext(BadHostnameConfiguration.class);
}
}
@Configuration
@@ -138,53 +191,41 @@ public class FeignClientValidationTests {
@FeignClient("foo_bar")
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
}
@Test
public void testMissingFallback() {
try (
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MissingFallbackConfiguration.class)) {
this.expected.expectMessage("No fallback instance of type");
assertNotNull(context.getBean(MissingFallbackConfiguration.Client.class));
}
}
@Configuration
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = MissingFallbackConfiguration.Client.class)
protected static class MissingFallbackConfiguration {
@FeignClient(name = "foobar", url = "http://localhost", fallback = ClientFallback.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class ClientFallback implements Client {
@Override
public String get() {
return null;
}
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
}
@Test
public void testWrongFallbackType() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
WrongFallbackTypeConfiguration.class)) {
this.expected.expectMessage("Incompatible fallback instance");
assertNotNull(context.getBean(WrongFallbackTypeConfiguration.Client.class));
@FeignClient(name = "foobar", url = "http://localhost", fallback = ClientFallback.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class ClientFallback implements Client {
@Override
public String get() {
return null;
}
}
}
@Configuration
@@ -192,34 +233,28 @@ public class FeignClientValidationTests {
@EnableFeignClients(clients = WrongFallbackTypeConfiguration.Client.class)
protected static class WrongFallbackTypeConfiguration {
@FeignClient(name = "foobar", url = "http://localhost", fallback = Dummy.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
@Bean
Dummy dummy() {
return new Dummy();
}
class Dummy {
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
}
@FeignClient(name = "foobar", url = "http://localhost", fallback = Dummy.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
@Test
public void testMissingFallbackFactory() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MissingFallbackFactoryConfiguration.class)) {
this.expected.expectMessage("No fallbackFactory instance of type");
assertNotNull(context.getBean(MissingFallbackFactoryConfiguration.Client.class));
}
class Dummy {
}
}
@Configuration
@@ -227,10 +262,17 @@ public class FeignClientValidationTests {
@EnableFeignClients(clients = MissingFallbackFactoryConfiguration.Client.class)
protected static class MissingFallbackFactoryConfiguration {
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = ClientFallback.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class ClientFallback implements FallbackFactory<Client> {
@@ -239,21 +281,9 @@ public class FeignClientValidationTests {
public Client create(Throwable cause) {
return null;
}
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
}
@Test
public void testWrongFallbackFactoryType() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
WrongFallbackFactoryTypeConfiguration.class)) {
this.expected.expectMessage("Incompatible fallbackFactory instance");
assertNotNull(context.getBean(WrongFallbackFactoryTypeConfiguration.Client.class));
}
}
@Configuration
@@ -261,24 +291,28 @@ public class FeignClientValidationTests {
@EnableFeignClients(clients = WrongFallbackFactoryTypeConfiguration.Client.class)
protected static class WrongFallbackFactoryTypeConfiguration {
@FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = Dummy.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
@Bean
Dummy dummy() {
return new Dummy();
}
class Dummy {
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = Dummy.class)
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
String get();
}
class Dummy {
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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.
@@ -23,10 +23,11 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import static org.junit.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -56,13 +57,13 @@ public class CachingSpringLoadBalancerFactoryTests {
when(this.delegate.getClientConfig("client2")).thenReturn(config);
this.factory = new CachingSpringLoadBalancerFactory(this.delegate,
loadBalancedRetryFactory);
this.loadBalancedRetryFactory);
}
@Test
public void delegateCreatesWhenMissing() {
FeignLoadBalancer client = this.factory.create("client1");
assertNotNull("client was null", client);
assertThat(client).as("client was null").isNotNull();
verify(this.delegate, times(1)).getClientConfig("client1");
}
@@ -70,10 +71,10 @@ public class CachingSpringLoadBalancerFactoryTests {
@Test
public void cacheWorks() {
FeignLoadBalancer client = this.factory.create("client2");
assertNotNull("client was null", client);
assertThat(client).as("client was null").isNotNull();
client = this.factory.create("client2");
assertNotNull("client was null", client);
assertThat(client).as("client was null").isNotNull();
verify(this.delegate, times(1)).getClientConfig("client2");
}
@@ -84,9 +85,10 @@ public class CachingSpringLoadBalancerFactoryTests {
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 500);
when(this.delegate.getClientConfig("retry")).thenReturn(config);
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(this.delegate);
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(
this.delegate);
FeignLoadBalancer client = this.factory.create("retry");
assertNotNull("client was null", client);
assertThat(client).as("client was null").isNotNull();
}
@Test
@@ -95,8 +97,10 @@ public class CachingSpringLoadBalancerFactoryTests {
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 500);
when(this.delegate.getClientConfig("retry")).thenReturn(config);
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(this.delegate, loadBalancedRetryFactory);
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(
this.delegate, this.loadBalancedRetryFactory);
FeignLoadBalancer client = this.factory.create("retry");
assertNotNull("client was null", client);
assertThat(client).as("client was null").isNotNull();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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.ribbon;
@@ -53,8 +52,7 @@ import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout;
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES;
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
import static feign.Request.HttpMethod.GET;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@@ -64,8 +62,10 @@ public class FeignLoadBalancerTests {
@Mock
private Client delegate;
@Mock
private ILoadBalancer lb;
@Mock
private IClientConfig config;
@@ -74,6 +74,7 @@ public class FeignLoadBalancerTests {
private ServerIntrospector inspector = new DefaultServerIntrospector();
private Integer defaultConnectTimeout = 10000;
private Integer defaultReadTimeout = 10000;
@Before
@@ -95,27 +96,19 @@ public class FeignLoadBalancerTests {
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
this.inspector);
Request request = new RequestTemplate()
.method(GET)
.target("http://foo/")
.resolve(new HashMap<>())
.request();
Request request = new RequestTemplate().method(GET).target("http://foo/")
.resolve(new HashMap<>()).request();
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
new URI(request.url()));
Response response = Response.builder()
.request(request)
.status(200)
.reason("Test")
.headers(Collections.emptyMap())
.body(new byte[0])
.build();
Response response = Response.builder().request(request).status(200).reason("Test")
.headers(Collections.emptyMap()).body(new byte[0]).build();
when(this.delegate.execute(any(Request.class), any(Options.class)))
.thenReturn(response);
RibbonResponse resp = this.feignLoadBalancer.execute(ribbonRequest, null);
assertThat(resp.getRequestedURI(), is(new URI("http://foo")));
assertThat(resp.getRequestedURI()).isEqualTo(new URI("http://foo"));
}
@Test
@@ -126,7 +119,7 @@ public class FeignLoadBalancerTests {
Server server = new Server("foo", 7777);
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
new URI("http://foo/"));
assertThat(uri, is(new URI("https://foo:7777/")));
assertThat(uri).isEqualTo(new URI("https://foo:7777/"));
}
@Test
@@ -148,7 +141,7 @@ public class FeignLoadBalancerTests {
Server server = new Server("foo", 7777);
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
new URI("http://foo/"));
assertThat(uri, is(new URI("http://foo:7777/")));
assertThat(uri).isEqualTo(new URI("http://foo:7777/"));
}
@Test
@@ -160,21 +153,22 @@ public class FeignLoadBalancerTests {
when(server.getHost()).thenReturn("foo");
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
new URI("http://bar/"));
assertThat(uri, is(new URI("https://foo:443/")));
assertThat(uri).isEqualTo(new URI("https://foo:443/"));
}
@Test
public void testRibbonRequestURLEncode() throws Exception {
String url = "http://foo/?name=%7bcookie";//name={cookie
String url = "http://foo/?name=%7bcookie"; // name={cookie
Request request = Request.create(GET, url, new HashMap<>(), null, null);
assertThat(request.url(),is(url));
assertThat(request.url()).isEqualTo(url);
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate,request,new URI(request.url()));
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
new URI(request.url()));
Request cloneRequest = ribbonRequest.toRequest();
assertThat(cloneRequest.url(),is(url));
assertThat(cloneRequest.url()).isEqualTo(url);
}
@@ -193,23 +187,23 @@ public class FeignLoadBalancerTests {
this.feignLoadBalancer = new FeignLoadBalancer(baseLoadBalancer, this.config,
this.inspector) {
protected void customizeLoadBalancerCommandBuilder(final FeignLoadBalancer.RibbonRequest request, final IClientConfig config,
final LoadBalancerCommand.Builder<FeignLoadBalancer.RibbonResponse> builder) {
protected void customizeLoadBalancerCommandBuilder(
final FeignLoadBalancer.RibbonRequest request,
final IClientConfig config,
final LoadBalancerCommand.Builder<FeignLoadBalancer.RibbonResponse> builder) {
builder.withServerLocator(request.getRequest().headers().get("c_ip"));
}
};
Request request = new RequestTemplate().method(GET).resolve(new HashMap<>()).request();
RibbonResponse resp = this.feignLoadBalancer.executeWithLoadBalancer(new RibbonRequest(this.delegate, request,
new URI(request.url())), null);
assertThat(resp.getRequestedURI().getPort(), is(7777));
request = new RequestTemplate()
.method(GET)
.header("c_ip", "666")
.resolve(new HashMap<>())
Request request = new RequestTemplate().method(GET).resolve(new HashMap<>())
.request();
resp = this.feignLoadBalancer.executeWithLoadBalancer(new RibbonRequest(this.delegate, request,
new URI(request.url())), null);
assertThat(resp.getRequestedURI().getPort(), is(6666));
RibbonResponse resp = this.feignLoadBalancer.executeWithLoadBalancer(
new RibbonRequest(this.delegate, request, new URI(request.url())), null);
assertThat(resp.getRequestedURI().getPort()).isEqualTo(7777);
request = new RequestTemplate().method(GET).header("c_ip", "666")
.resolve(new HashMap<>()).request();
resp = this.feignLoadBalancer.executeWithLoadBalancer(
new RibbonRequest(this.delegate, request, new URI(request.url())), null);
assertThat(resp.getRequestedURI().getPort()).isEqualTo(6666);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -25,7 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.openfeign.EnableFeignClients;
@@ -40,21 +39,18 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author Venil Noronha
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignRibbonClientPathTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,value = {
"spring.application.name=feignribbonclientpathtest",
"feign.okhttp.enabled=false",
"feign.httpclient.enabled=false",
"feign.hystrix.enabled=false",
@SpringBootTest(classes = FeignRibbonClientPathTests.Application.class, webEnvironment = RANDOM_PORT, value = {
"spring.application.name=feignribbonclientpathtest", "feign.okhttp.enabled=false",
"feign.httpclient.enabled=false", "feign.hystrix.enabled=false",
"test.path.prefix=/base/path" // For pathWithPlaceholder test
}
)
})
@DirtiesContext
public class FeignRibbonClientPathTests {
@@ -72,51 +68,10 @@ public class FeignRibbonClientPathTests {
@Autowired
private TestClient4 testClient4;
@Autowired
private TestClient5 testClient5;
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
@FeignClient(name = "localapp", path = "/base/path")
protected interface TestClient1 extends TestClient { }
@FeignClient(name = "localapp1", path = "base/path")
protected interface TestClient2 extends TestClient { }
@FeignClient(name = "localapp2", path = "base/path/")
protected interface TestClient3 extends TestClient { }
@FeignClient(name = "localapp3", path = "/base/path/")
protected interface TestClient4 extends TestClient { }
@FeignClient(name = "localapp4", path = "${test.path.prefix}")
protected interface TestClient5 extends TestClient { }
@Configuration
@EnableAutoConfiguration
@RestController
@RequestMapping("/base/path")
@EnableFeignClients(clients = {
TestClient1.class, TestClient2.class, TestClient3.class, TestClient4.class,
TestClient5.class
})
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
public static class Application {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public Hello getHello() {
return new Hello("hello world");
}
}
@Test
public void pathWithLeadingButNotTrailingSlash() {
testClientPath(this.testClient1);
@@ -144,27 +99,79 @@ public class FeignRibbonClientPathTests {
private void testClientPath(TestClient testClient) {
Hello hello = testClient.getHello();
assertNotNull("Object returned was null", hello);
assertEquals("Response object value didn't match", "hello world",
hello.getMessage());
assertThat(hello).as("Object returned was null").isNotNull();
assertThat(hello.getMessage()).as("Response object value didn't match")
.isEqualTo("hello world");
}
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
}
@FeignClient(name = "localapp", path = "/base/path")
protected interface TestClient1 extends TestClient {
}
@FeignClient(name = "localapp1", path = "base/path")
protected interface TestClient2 extends TestClient {
}
@FeignClient(name = "localapp2", path = "base/path/")
protected interface TestClient3 extends TestClient {
}
@FeignClient(name = "localapp3", path = "/base/path/")
protected interface TestClient4 extends TestClient {
}
@FeignClient(name = "localapp4", path = "${test.path.prefix}")
protected interface TestClient5 extends TestClient {
}
@Configuration
@EnableAutoConfiguration
@RestController
@RequestMapping("/base/path")
@EnableFeignClients(clients = { TestClient1.class, TestClient2.class,
TestClient3.class, TestClient4.class, TestClient5.class })
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
@Import(NoSecurityConfiguration.class)
public static class Application {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
public Hello getHello() {
return new Hello("hello world");
}
}
public static class Hello {
private String message;
public Hello() {}
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return message;
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
}
@Configuration

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2017 the original author or authors.
* Copyright 2013-2019 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.
@@ -20,17 +20,19 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicInteger;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList;
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;
@@ -41,22 +43,20 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* Tests the Feign Retryer, not ribbon retry.
*
* @author Spencer Gibb
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
@SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class, webEnvironment = RANDOM_PORT, value = {
"spring.application.name=feignclientretrytest", "feign.okhttp.enabled=false",
"feign.httpclient.enabled=false", "feign.hystrix.enabled=false", "localapp.ribbon.MaxAutoRetries=2",
"localapp.ribbon.MaxAutoRetriesNextServer=3"})
"feign.httpclient.enabled=false", "feign.hystrix.enabled=false",
"localapp.ribbon.MaxAutoRetries=2",
"localapp.ribbon.MaxAutoRetriesNextServer=3" })
@DirtiesContext
public class FeignRibbonClientRetryTests {
@@ -66,13 +66,32 @@ public class FeignRibbonClientRetryTests {
@Autowired
private TestClient testClient;
@Test
public void testClient() {
assertThat(this.testClient).as("testClient was null").isNotNull();
assertThat(Proxy.isProxyClass(this.testClient.getClass()))
.as("testClient is not a java Proxy").isTrue();
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
}
@Test
public void testRetries() {
int retryMe = this.testClient.retryMe();
assertThat(1).as("retryCount didn't match").isEqualTo(retryMe);
// TODO: not sure how to verify retry happens. Debugging through it, it works
// maybe the assertEquals above is enough because of the bogus servers
}
@FeignClient("localapp")
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/retryme")
int retryMe();
}
@Configuration
@@ -94,27 +113,11 @@ public class FeignRibbonClientRetryTests {
public int retryMe() {
return this.retries.getAndIncrement();
}
}
@Test
public void testClient() {
assertNotNull("testClient was null", this.testClient);
assertTrue("testClient is not a java Proxy",
Proxy.isProxyClass(this.testClient.getClass()));
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
assertNotNull("invocationHandler was null", invocationHandler);
}
@Test
public void testRetries() {
int retryMe = this.testClient.retryMe();
assertEquals("retryCount didn't match", retryMe, 1);
// TODO: not sure how to verify retry happens. Debugging through it, it works
// maybe the assertEquals above is enough because of the bogus servers
}
public static class Hello {
private String message;
public Hello() {
@@ -125,13 +128,15 @@ public class FeignRibbonClientRetryTests {
}
public String getMessage() {
return message;
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
}
}
// Load balancer with fixed server list for "local" pointing to localhost
@@ -145,8 +150,8 @@ class LocalRibbonClientConfiguration {
@Bean
public ServerList<Server> ribbonServerList() {
return new StaticServerList<>(new Server("mybadhost", 80),
new Server("mybadhost2", 10002),
new Server("mybadhost3", 10003), new Server("localhost", this.port));
new Server("mybadhost2", 10002), new Server("mybadhost3", 10003),
new Server("localhost", this.port));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -52,6 +52,7 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat;
public class FeignRibbonClientTests {
private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class);
private Client delegate = mock(Client.class);
private SpringClientFactory factory = new SpringClientFactory() {
@@ -81,71 +82,58 @@ public class FeignRibbonClientTests {
// Even though we don't maintain FeignRibbonClient, keep these tests
// around to make sure the expected behaviour doesn't break
private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory), this.factory);
private Client client = new LoadBalancerFeignClient(this.delegate,
new CachingSpringLoadBalancerFactory(this.factory), this.factory);
@Before
public void init() {
when(this.loadBalancer.chooseServer(any())).thenReturn(
new Server("foo.com", 8000));
//to fix NPE
when(this.loadBalancer.chooseServer(any()))
.thenReturn(new Server("foo.com", 8000));
// to fix NPE
LoadBalancerStats stats = mock(LoadBalancerStats.class);
when(this.loadBalancer.getLoadBalancerStats()).thenReturn(stats);
when(stats.getSingleServerStat(any(Server.class))).thenReturn(mock(ServerStats.class));
when(stats.getSingleServerStat(any(Server.class)))
.thenReturn(mock(ServerStats.class));
}
@Test
public void remoteRequestIsSentAtRoot() throws Exception {
Request request = new RequestTemplate()
.method(GET)
.target("http://foo")
.resolve(new HashMap<>())
.request();
Request request = new RequestTemplate().method(GET).target("http://foo")
.resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/");
verify(this.delegate).execute(argThat(matcher),
any(Options.class));
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
@Test
public void remoteRequestIsSent() throws Exception {
Request request = new RequestTemplate()
.method(GET)
.target("http://foo/")
.resolve(new HashMap<>())
.request();
Request request = new RequestTemplate().method(GET).target("http://foo/")
.resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/");
verify(this.delegate).execute(argThat(matcher),
any(Options.class));
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
@Test
public void verifyCleanUrl() throws Exception {
Request request = new RequestTemplate()
.method(GET)
.target("http://tp/abc/bcd.json")
.resolve(new HashMap<>())
.request();
Request request = new RequestTemplate().method(GET)
.target("http://tp/abc/bcd.json").resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/abc/bcd.json");
verify(this.delegate).execute(argThat(matcher),
any(Options.class));
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
@Test
public void remoteRequestIsSecure() throws Exception {
Request request = new RequestTemplate()
.method(GET)
.target("https://foo/")
.resolve(new HashMap<>())
.request();
Request request = new RequestTemplate().method(GET).target("https://foo/")
.resolve(new HashMap<>()).request();
this.client.execute(request, new Options());
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/");
verify(this.delegate).execute(argThat(matcher),
any(Options.class));
verify(this.delegate).execute(argThat(matcher), any(Options.class));
}
private final static class RequestMatcher extends CustomMatcher<Request> {
private String url;
private RequestMatcher(String url) {
@@ -158,6 +146,7 @@ public class FeignRibbonClientTests {
Request request = (Request) item;
return request.url().equals(this.url);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,40 +12,40 @@
* 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.ribbon;
import java.lang.reflect.Field;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import org.apache.http.config.Lookup;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.impl.conn.DefaultHttpClientConnectionOperator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientRetryTests;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"debug=true","feign.httpclient.disableSslValidation=true"})
@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"debug=true", "feign.httpclient.disableSslValidation=true" })
@DirtiesContext
public class FeignRibbonHttpClientConfigurationTests {
@@ -54,21 +54,29 @@ public class FeignRibbonHttpClientConfigurationTests {
@Test
public void disableSslTest() throws Exception {
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager);
assertNotNull(socketFactoryRegistry.lookup("https"));
assertNull(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers());
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(
this.connectionManager);
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers())
.isNull();
}
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(HttpClientConnectionManager connectionManager) {
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator)this.getField(connectionManager, "connectionOperator");
return (Lookup)this.getField(connectionOperator, "socketFactoryRegistry");
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
HttpClientConnectionManager connectionManager) {
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this
.getField(connectionManager, "connectionOperator");
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
}
private X509TrustManager getX509TrustManager(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory)socketFactoryRegistry.lookup("https");
SSLSocketFactory sslSocketFactory = (SSLSocketFactory)this.getField(connectionSocketFactory, "socketfactory");
SSLContextSpi sslContext = (SSLContextSpi)this.getField(sslSocketFactory, "context");
return (X509TrustManager)this.getField(sslContext, "trustManager");
private X509TrustManager getX509TrustManager(
Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry
.lookup("https");
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this
.getField(connectionSocketFactory, "socketfactory");
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory,
"context");
return (X509TrustManager) this.getField(sslContext, "trustManager");
}
protected <T> Object getField(Object target, String name) {
@@ -81,9 +89,12 @@ public class FeignRibbonHttpClientConfigurationTests {
@Configuration
@EnableAutoConfiguration
static class FeignRibbonHttpClientConfigurationTestsApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,36 +12,37 @@
* 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.ribbon;
import okhttp3.OkHttpClient;
import java.lang.reflect.Field;
import javax.net.ssl.HostnameVerifier;
import org.junit.Assert;
import okhttp3.OkHttpClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientRetryTests;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.FeignRibbonOkHttpClientConfigurationTestsApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"debug=true","feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false"})
@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.FeignRibbonOkHttpClientConfigurationTestsApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"debug=true", "feign.httpclient.disableSslValidation=true",
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false" })
@DirtiesContext
public class FeignRibbonOkHttpClientConfigurationTests {
@@ -50,8 +51,11 @@ public class FeignRibbonOkHttpClientConfigurationTests {
@Test
public void disableSslTest() throws Exception {
HostnameVerifier hostnameVerifier = (HostnameVerifier)this.getField(httpClient, "hostnameVerifier");
Assert.assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier));
HostnameVerifier hostnameVerifier = (HostnameVerifier) this
.getField(this.httpClient, "hostnameVerifier");
assertThat(
OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier))
.isTrue();
}
protected <T> Object getField(Object target, String name) {
@@ -64,9 +68,12 @@ public class FeignRibbonOkHttpClientConfigurationTests {
@Configuration
@EnableAutoConfiguration
static class FeignRibbonOkHttpClientConfigurationTestsApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
.run(args);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2019 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,12 @@
package org.springframework.cloud.openfeign.ribbon;
import static org.junit.Assert.assertEquals;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import feign.Request;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@@ -33,10 +35,7 @@ import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RequestMapping;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import feign.Request;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Spencer Gibb
@@ -56,20 +55,20 @@ public class LoadBalancerFeignClientOverrideTests {
// specific ribbon 'bar' configuration via spring bean
Request.Options barOptions = this.context.getInstance("bar",
Request.Options.class);
assertEquals(1, barOptions.connectTimeoutMillis());
assertEquals(2, barOptions.readTimeoutMillis());
assertThat(barOptions.connectTimeoutMillis()).isEqualTo(1);
assertThat(barOptions.readTimeoutMillis()).isEqualTo(2);
assertOptions(barOptions, "bar", 1, 2);
// specific ribbon 'foo' configuration via application.yml
Request.Options fooOptions = this.context.getInstance("foo",
Request.Options.class);
assertEquals(LoadBalancerFeignClient.DEFAULT_OPTIONS, fooOptions);
assertThat(fooOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS);
assertOptions(fooOptions, "foo", 7, 17);
// generic ribbon default configuration
Request.Options bazOptions = this.context.getInstance("baz",
Request.Options.class);
assertEquals(LoadBalancerFeignClient.DEFAULT_OPTIONS, bazOptions);
assertThat(bazOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS);
assertOptions(bazOptions, "baz", 3001, 60001);
}
@@ -78,44 +77,54 @@ public class LoadBalancerFeignClientOverrideTests {
LoadBalancerFeignClient client = this.context.getInstance(name,
LoadBalancerFeignClient.class);
IClientConfig config = client.getClientConfig(options, name);
assertEquals("connect was wrong for " + name, expectedConnect,
config.get(CommonClientConfigKey.ConnectTimeout, -1).intValue());
assertEquals("read was wrong for " + name, expectedRead,
config.get(CommonClientConfigKey.ReadTimeout, -1).intValue());
assertThat(config.get(CommonClientConfigKey.ConnectTimeout, -1).intValue())
.as("connect was wrong for " + name).isEqualTo(expectedConnect);
assertThat(config.get(CommonClientConfigKey.ReadTimeout, -1).intValue())
.as("read was wrong for " + name).isEqualTo(expectedRead);
}
@FeignClient(value = "foo", configuration = FooConfiguration.class)
interface FooClient {
@RequestMapping("/")
String get();
}
@FeignClient(value = "bar", configuration = BarConfiguration.class)
interface BarClient {
@RequestMapping("/")
String get();
}
@FeignClient("baz")
interface BazClient {
@RequestMapping("/")
String get();
}
@Configuration
@EnableFeignClients(clients = { FooClient.class, BarClient.class, BazClient.class })
@EnableAutoConfiguration
protected static class TestConfiguration {
}
@FeignClient(value = "foo", configuration = FooConfiguration.class)
interface FooClient {
@RequestMapping("/")
String get();
}
public static class FooConfiguration {
}
@FeignClient(value = "bar", configuration = BarConfiguration.class)
interface BarClient {
@RequestMapping("/")
String get();
}
public static class BarConfiguration {
@Bean
public Request.Options feignRequestOptions() {
return new Request.Options(1, 2);
}
}
@FeignClient("baz")
interface BazClient {
@RequestMapping("/")
String get();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -12,7 +12,6 @@
* 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.ribbon;
@@ -36,7 +35,6 @@ import com.netflix.loadbalancer.Server;
import feign.Client;
import feign.Request;
import feign.Response;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -69,10 +67,7 @@ import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO
import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER;
import static feign.Request.HttpMethod.GET;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -90,13 +85,17 @@ import static org.mockito.Mockito.when;
* @author Olga Maciaszek-Sharma
*/
public class RetryableFeignLoadBalancerTests {
@Mock
private ILoadBalancer lb;
@Mock
private IClientConfig config;
private ServerIntrospector inspector = new DefaultServerIntrospector();
private Integer defaultConnectTimeout = 10000;
private Integer defaultReadTimeout = 10000;
@Before
@@ -114,31 +113,38 @@ public class RetryableFeignLoadBalancerTests {
@Test
public void executeNoFailure() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404,502,foo, ,").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory);
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response response = Response.builder()
.status(200)
.request(feignRequest)
.headers(new HashMap<>())
.build();
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response response = Response.builder().status(200).request(feignRequest)
.headers(new HashMap<>()).build();
doReturn(response).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertEquals(200, ribbonResponse.toResponse().status());
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(1)).execute(any(Request.class), any(Request.Options.class));
}
@@ -147,48 +153,62 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
doThrow(new IOException("boom")).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, new LoadBalancedRetryFactory() {
@Override
public LoadBalancedRetryPolicy createRetryPolicy(String s, ServiceInstanceChooser serviceInstanceChooser) {
return null;
}
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
doThrow(new IOException("boom")).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
this.config, this.inspector, new LoadBalancedRetryFactory() {
@Override
public LoadBalancedRetryPolicy createRetryPolicy(String s,
ServiceInstanceChooser serviceInstanceChooser) {
return null;
}
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[0];
}
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[0];
}
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return null;
}
});
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return null;
}
});
try {
feignLb.execute(request, null);
} catch(Exception e) {
assertThat(e, instanceOf(IOException.class));
} finally {
verify(client, times(1)).execute(any(Request.class), any(Request.Options.class));
}
catch (Exception e) {
assertThat(e).isInstanceOf(IOException.class);
}
finally {
verify(client, times(1)).execute(any(Request.class),
any(Request.Options.class));
}
}
@Test
public void executeRetry() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
@@ -197,36 +217,43 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response response = Response.builder()
.status(200)
.request(feignRequest)
.headers(new HashMap<>())
.build();
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response response = Response.builder().status(200).request(feignRequest)
.headers(new HashMap<>()).build();
doThrow(new IOException("boom")).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertEquals(200, ribbonResponse.toResponse().status());
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertEquals(1, backOffPolicy.getCount());
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void executeRetryOnStatusCode() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
@@ -235,168 +262,182 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response response = Response
.builder()
.request(feignRequest)
.status(200)
.headers(new HashMap<>())
.build();
Response fourOFourResponse = Response.builder()
.request(feignRequest)
.status(404)
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
Response fourOFourResponse = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).build();
doReturn(fourOFourResponse).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertEquals(200, ribbonResponse.toResponse().status());
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertEquals(1, backOffPolicy.getCount());
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
@Test
public void executeRetryOnStatusCodeWithEmptyBody() throws Exception {
int retriesNextServer = 0;
when(this.config.get(MaxAutoRetriesNextServer,
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(retriesNextServer);
doReturn(new Server("foo", 80)).when(lb).chooseServer(any());
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(retriesNextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response response = Response.builder()
.request(feignRequest)
.status(404)
.headers(new HashMap<>())
.build();
Response fourOFourResponse = Response.builder()
.request(feignRequest)
.status(404)
.headers(new HashMap<>())
.build();
doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertEquals(404, ribbonResponse.toResponse().status());
assertEquals(Integer.valueOf(0), ribbonResponse.toResponse().body().length());
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertEquals(1, backOffPolicy.getCount());
int retriesNextServer = 0;
when(this.config.get(MaxAutoRetriesNextServer,
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(retriesNextServer);
doReturn(new Server("foo", 80)).when(this.lb).chooseServer(any());
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(retriesNextServer).when(config)
.get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
}
};
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response response = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).build();
Response fourOFourResponse = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).build();
doReturn(fourOFourResponse).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertThat(ribbonResponse.toResponse().status()).isEqualTo(404);
assertThat(ribbonResponse.toResponse().body().length())
.isEqualTo(Integer.valueOf(0));
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void getRequestSpecificRetryHandler() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory);
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response response = Response.builder()
.request(feignRequest)
.status(200)
.headers(new HashMap<>())
.build();
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
RequestSpecificRetryHandler retryHandler = feignLb.getRequestSpecificRetryHandler(request, config);
assertEquals(1, retryHandler.getMaxRetriesOnNextServer());
assertEquals(1, retryHandler.getMaxRetriesOnSameServer());
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doReturn(response).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
this.config, this.inspector, loadBalancedRetryFactory);
RequestSpecificRetryHandler retryHandler = feignLb
.getRequestSpecificRetryHandler(request, this.config);
assertThat(retryHandler.getMaxRetriesOnNextServer()).isEqualTo(1);
assertThat(retryHandler.getMaxRetriesOnSameServer()).isEqualTo(1);
}
@Test
public void choose() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory);
Request feignRequest = Request
.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory);
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response response = Response.builder()
.request(feignRequest)
.status(200).headers(new HashMap<>())
.build();
doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doReturn(response).when(client).execute(any(Request.class),
any(Request.Options.class));
final Server server = new Server("foo", 80);
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(new ILoadBalancer() {
@Override
public void addServers(List<Server> list) {
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(
new ILoadBalancer() {
@Override
public void addServers(List<Server> list) {
}
}
@Override
public Server chooseServer(Object o) {
return server;
}
@Override
public Server chooseServer(Object o) {
return server;
}
@Override
public void markServerDown(Server server) {
@Override
public void markServerDown(Server server) {
}
}
@Override
public List<Server> getServerList(boolean b) {
return null;
}
@Override
public List<Server> getServerList(boolean b) {
return null;
}
@Override
public List<Server> getReachableServers() {
return null;
}
@Override
public List<Server> getReachableServers() {
return null;
}
@Override
public List<Server> getAllServers() {
return null;
}
}, config, inspector, loadBalancedRetryFactory);
@Override
public List<Server> getAllServers() {
return null;
}
}, this.config, this.inspector, loadBalancedRetryFactory);
ServiceInstance serviceInstance = feignLb.choose("foo");
assertEquals("foo", serviceInstance.getHost());
assertEquals(80, serviceInstance.getPort());
assertThat(serviceInstance.getHost()).isEqualTo("foo");
assertThat(serviceInstance.getPort()).isEqualTo(80);
}
@Test
public void retryListenerTest() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
MyRetryListener myRetryListener = new MyRetryListener();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory) {
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[]{myRetryListener};
return new RetryListener[] { myRetryListener };
}
@Override
@@ -407,41 +448,48 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener"));
Response response = Response.builder()
.request(feignRequest)
.status(200)
.headers(new HashMap<>())
.build();
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://listener"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doThrow(new IOException("boom")).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertEquals(200, ribbonResponse.toResponse().status());
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertEquals(1, backOffPolicy.getCount());
assertEquals(1, myRetryListener.getOnError());
assertThat(backOffPolicy.getCount()).isEqualTo(1);
assertThat(myRetryListener.getOnError()).isEqualTo(1);
}
@Test(expected = TerminatedRetryException.class)
public void retryListenerTestNoRetry() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
MyRetryListenerNotRetry myRetryListenerNotRetry = new MyRetryListenerNotRetry();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public RetryListener[] createRetryListeners(String service) {
return new RetryListener[]{myRetryListenerNotRetry};
return new RetryListener[] { myRetryListenerNotRetry };
}
@Override
@@ -452,26 +500,35 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener"));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://listener"));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
}
@Test
public void retryWithDefaultConstructorTest() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq(""));
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
RibbonLoadBalancedRetryFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
@@ -480,36 +537,43 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener"));
Response response = Response.builder()
.request(feignRequest)
.status(200)
.headers(new HashMap<>())
.build();
doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://listener"));
Response response = Response.builder().request(feignRequest).status(200)
.headers(new HashMap<>()).build();
doThrow(new IOException("boom")).doReturn(response).when(client)
.execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryPolicyFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
assertEquals(200, ribbonResponse.toResponse().status());
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertEquals(1, backOffPolicy.getCount());
assertThat(backOffPolicy.getCount()).isEqualTo(1);
}
@Test
public void executeRetryFail() throws Exception {
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config);
RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb,
this.config);
lbContext.setRetryHandler(new DefaultLoadBalancerRetryHandler(1, 0, true));
SpringClientFactory clientFactory = mock(SpringClientFactory.class);
IClientConfig config = mock(IClientConfig.class);
doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
doReturn(0).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(0).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer),
anyInt());
doReturn(true).when(config)
.get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
doReturn(this.defaultConnectTimeout).when(config)
.get(eq(CommonClientConfigKey.ConnectTimeout));
doReturn(this.defaultReadTimeout).when(config)
.get(eq(CommonClientConfigKey.ReadTimeout));
doReturn("404").when(config).getPropertyAsString(
eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq(""));
doReturn(config).when(clientFactory).getClientConfig(eq("default"));
doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class));
MyBackOffPolicy backOffPolicy = new MyBackOffPolicy();
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){
RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(
clientFactory) {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return backOffPolicy;
@@ -518,12 +582,11 @@ public class RetryableFeignLoadBalancerTests {
Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(),
new byte[] {}, UTF_8);
Client client = mock(Client.class);
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo"));
Response fourOFourResponse = Response.builder()
.request(feignRequest)
.status(404)
.headers(new HashMap<>())
.body(new Response.Body() { //set content into response
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
client, feignRequest, new URI("http://foo"));
Response fourOFourResponse = Response.builder().request(feignRequest).status(404)
.headers(new HashMap<>()).body(new Response.Body() { // set content into
// response
@Override
public Integer length() {
return "test".getBytes().length;
@@ -553,15 +616,17 @@ public class RetryableFeignLoadBalancerTests {
public void close() throws IOException {
}
}).build();
doReturn(fourOFourResponse).when(client).execute(any(Request.class), any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory);
doReturn(fourOFourResponse).when(client).execute(any(Request.class),
any(Request.Options.class));
RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb,
config, this.inspector, loadBalancedRetryFactory);
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
assertEquals(1, backOffPolicy.getCount());
assertThat(backOffPolicy.getCount()).isEqualTo(1);
InputStream inputStream = ribbonResponse.toResponse().body().asInputStream();
byte[] buf = new byte[100];
int read = inputStream.read(buf);
Assert.assertThat(new String(buf, 0, read), is("test"));
assertThat(new String(buf, 0, read)).isEqualTo("test");
}
class MyBackOffPolicy implements BackOffPolicy {
@@ -574,12 +639,13 @@ public class RetryableFeignLoadBalancerTests {
}
@Override
public void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
count++;
public void backOff(BackOffContext backOffContext)
throws BackOffInterruptedException {
this.count++;
}
public int getCount() {
return count;
return this.count;
}
}
@@ -589,39 +655,48 @@ public class RetryableFeignLoadBalancerTests {
private int onError = 0;
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
public <T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback) {
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
onError++;
public <T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
this.onError++;
}
public int getOnError() {
return onError;
return this.onError;
}
}
class MyRetryListenerNotRetry implements RetryListener {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
public <T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback) {
return false;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {}
public <T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.ribbon;
import java.io.ByteArrayInputStream;
@@ -33,7 +34,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.util.StreamUtils;
import static feign.Request.HttpMethod.GET;
import static org.junit.Assert.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
@@ -48,15 +49,19 @@ public class RibbonResponseStatusCodeExceptionTest {
fooValues.add("bar");
headers.put("foo", fooValues);
Request request = Request.create(GET, "http://service.com",
new HashMap<String, Collection<String>>(), new byte[]{}, Charset.defaultCharset());
new HashMap<String, Collection<String>>(), new byte[] {},
Charset.defaultCharset());
byte[] body = "foo".getBytes();
ByteArrayInputStream is = new ByteArrayInputStream(body);
Response response = Response.builder().status(200).reason("Success").request(request).body(is, body.length).headers(headers).build();
RibbonResponseStatusCodeException ex = new RibbonResponseStatusCodeException("service", response, body,
new URI(request.url()));
assertEquals(200, ex.getResponse().status());
assertEquals(request, ex.getResponse().request());
assertEquals("Success", ex.getResponse().reason());
assertEquals("foo", StreamUtils.copyToString(ex.getResponse().body().asInputStream(), Charset.defaultCharset()));
Response response = Response.builder().status(200).reason("Success")
.request(request).body(is, body.length).headers(headers).build();
RibbonResponseStatusCodeException ex = new RibbonResponseStatusCodeException(
"service", response, body, new URI(request.url()));
assertThat(ex.getResponse().status()).isEqualTo(200);
assertThat(ex.getResponse().request()).isEqualTo(request);
assertThat(ex.getResponse().reason()).isEqualTo("Success");
assertThat(StreamUtils.copyToString(ex.getResponse().body().asInputStream(),
Charset.defaultCharset())).isEqualTo("foo");
}
}
}

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