Removes ribbon and hystrix support
This commit is contained in:
@@ -79,19 +79,6 @@ public class FeignAutoConfiguration {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
|
||||
protected static class HystrixFeignTargeterConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Targeter feignTargeter() {
|
||||
return new HystrixTargeter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
|
||||
protected static class DefaultFeignTargeterConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import feign.hystrix.FallbackFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
@@ -88,13 +86,6 @@ public class FeignClientBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> fallbackFactory(
|
||||
final Class<? extends FallbackFactory<? extends T>> fallbackFactory) {
|
||||
FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
|
||||
this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the created Feign client
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -288,11 +287,6 @@ class FeignClientFactoryBean
|
||||
String url = this.url + cleanPath();
|
||||
Client client = getOptional(context, Client.class);
|
||||
if (client != null) {
|
||||
if (client instanceof LoadBalancerFeignClient) {
|
||||
// not load balancing because we have a url,
|
||||
// but ribbon is on the classpath, so unwrap
|
||||
client = ((LoadBalancerFeignClient) client).getDelegate();
|
||||
}
|
||||
if (client instanceof FeignBlockingLoadBalancerClient) {
|
||||
// not load balancing because we have a url,
|
||||
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
|
||||
|
||||
@@ -20,14 +20,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
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;
|
||||
@@ -35,7 +33,6 @@ 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.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.openfeign.support.PageJacksonModule;
|
||||
@@ -144,18 +141,4 @@ public class FeignClientsConfiguration {
|
||||
return new PageJacksonModule();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
import feign.hystrix.SetterFactory;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Erik Kringen
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
class HystrixTargeter implements Targeter {
|
||||
|
||||
@Override
|
||||
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;
|
||||
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
|
||||
: factory.getContextId();
|
||||
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
|
||||
if (setterFactory != null) {
|
||||
builder.setterFactory(setterFactory);
|
||||
}
|
||||
Class<?> fallback = factory.getFallback();
|
||||
if (fallback != void.class) {
|
||||
return targetWithFallback(name, context, target, builder, fallback);
|
||||
}
|
||||
Class<?> fallbackFactory = factory.getFallbackFactory();
|
||||
if (fallbackFactory != void.class) {
|
||||
return targetWithFallbackFactory(name, 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);
|
||||
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());
|
||||
return builder.target(target, fallbackInstance);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
return (T) fallbackInstance;
|
||||
}
|
||||
|
||||
private <T> T getOptional(String feignClientName, FeignContext context,
|
||||
Class<T> beanType) {
|
||||
return context.getInstance(feignClientName, beanType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,14 +19,12 @@ package org.springframework.cloud.openfeign.loadbalancer;
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -43,7 +41,6 @@ import org.springframework.context.annotation.Import;
|
||||
@ConditionalOnClass(Feign.class)
|
||||
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(FeignHttpClientProperties.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
// Order is important here, last should be the default, first should be optional
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.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;
|
||||
|
||||
/**
|
||||
* Factory for SpringLoadBalancer instances that caches the entries created.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
* @author Gang Li
|
||||
*/
|
||||
public class CachingSpringLoadBalancerFactory {
|
||||
|
||||
protected final SpringClientFactory factory;
|
||||
|
||||
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
|
||||
|
||||
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
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) {
|
||||
return client;
|
||||
}
|
||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class DefaultFeignLoadBalancedConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||
SpringClientFactory clientFactory) {
|
||||
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
|
||||
clientFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||
import com.netflix.client.ClientException;
|
||||
import com.netflix.client.ClientRequest;
|
||||
import com.netflix.client.IResponse;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.RetryHandler;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecureConnectionIfNeeded;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
* @author Ryan Baxter
|
||||
* @author Tim Ysewyn
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
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) {
|
||||
super(lb, clientConfig);
|
||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
||||
this.clientConfig = clientConfig;
|
||||
this.ribbon = RibbonProperties.from(clientConfig);
|
||||
RibbonProperties ribbon = this.ribbon;
|
||||
this.connectTimeout = ribbon.getConnectTimeout();
|
||||
this.readTimeout = ribbon.getReadTimeout();
|
||||
this.serverIntrospector = serverIntrospector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
|
||||
throws IOException {
|
||||
Request.Options options;
|
||||
if (configOverride != null) {
|
||||
RibbonProperties override = RibbonProperties.from(configOverride);
|
||||
options = new Request.Options(override.connectTimeout(this.connectTimeout),
|
||||
override.readTimeout(this.readTimeout));
|
||||
}
|
||||
else {
|
||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||
}
|
||||
Response response = request.client().execute(request.toRequest(), options);
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||
RibbonRequest request, IClientConfig requestConfig) {
|
||||
if (this.ribbon.isOkToRetryOnAllOperations()) {
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
if (!request.toRequest().httpMethod().name().equals("GET")) {
|
||||
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
else {
|
||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI reconstructURIWithServer(Server server, URI original) {
|
||||
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) {
|
||||
this.client = client;
|
||||
setUri(uri);
|
||||
this.request = toRequest(request);
|
||||
}
|
||||
|
||||
private Request toRequest(Request request) {
|
||||
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
||||
request.headers());
|
||||
return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
|
||||
request.requestBody().asBytes(), request.charset());
|
||||
}
|
||||
|
||||
Request toRequest() {
|
||||
return toRequest(this.request);
|
||||
}
|
||||
|
||||
Client client() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
HttpRequest toHttpRequest() {
|
||||
return new HttpRequest() {
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return HttpMethod
|
||||
.resolve(RibbonRequest.this.toRequest().httpMethod().name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodValue() {
|
||||
return getMethod().name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return RibbonRequest.this.getUri();
|
||||
}
|
||||
|
||||
@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()) {
|
||||
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
|
||||
}
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(headers);
|
||||
return httpHeaders;
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Request getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
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) {
|
||||
this.uri = uri;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPayload() throws ClientException {
|
||||
return this.response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPayload() {
|
||||
return this.response.body() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return this.response.status() == 200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getRequestedURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return this.response.headers();
|
||||
}
|
||||
|
||||
Response toResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (this.response != null && this.response.body() != null) {
|
||||
this.response.body().close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.http.HttpRequest;
|
||||
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) {
|
||||
super(request, policy, serviceInstanceChooser, serviceName);
|
||||
this.request = request;
|
||||
this.serviceId = serviceName;
|
||||
}
|
||||
|
||||
@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 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) {
|
||||
return true;
|
||||
}
|
||||
return super.canRetry(context);
|
||||
}
|
||||
|
||||
@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.
|
||||
*/
|
||||
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) {
|
||||
this.serviceId = serviceId;
|
||||
this.request = request;
|
||||
this.metadata = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return this.serviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return this.request.getURI().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return this.request.getURI().getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return "https".equals(this.request.getURI().getScheme());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return this.request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.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.autoconfigure.condition.ConditionalOnProperty;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
|
||||
* load balancer.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
|
||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
|
||||
matchIfMissing = true)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@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
|
||||
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
|
||||
OkHttpFeignLoadBalancedConfiguration.class,
|
||||
DefaultFeignLoadBalancedConfiguration.class })
|
||||
public class FeignRibbonClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
|
||||
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
|
||||
SpringClientFactory factory) {
|
||||
return new CachingSpringLoadBalancerFactory(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
|
||||
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
|
||||
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Request.Options feignRequestOptions() {
|
||||
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import org.apache.http.client.HttpClient;
|
||||
|
||||
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.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ApacheHttpClient.class)
|
||||
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
||||
@Import(HttpClientFeignConfiguration.class)
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
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) {
|
||||
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 {
|
||||
URI asUri = URI.create(request.url());
|
||||
String clientName = asUri.getHost();
|
||||
URI uriWithoutHost = cleanUrl(request.url(), clientName);
|
||||
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
|
||||
this.delegate, request, uriWithoutHost);
|
||||
|
||||
IClientConfig requestConfig = getClientConfig(options, clientName);
|
||||
return lbClient(clientName)
|
||||
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
|
||||
}
|
||||
catch (ClientException e) {
|
||||
IOException io = findIOException(e);
|
||||
if (io != null) {
|
||||
throw io;
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
IClientConfig getClientConfig(Request.Options options, String clientName) {
|
||||
IClientConfig requestConfig;
|
||||
if (options == DEFAULT_OPTIONS) {
|
||||
requestConfig = this.clientFactory.getClientConfig(clientName);
|
||||
}
|
||||
else {
|
||||
requestConfig = new FeignOptionsClientConfig(options);
|
||||
}
|
||||
return requestConfig;
|
||||
}
|
||||
|
||||
protected IOException findIOException(Throwable t) {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
if (t instanceof IOException) {
|
||||
return (IOException) t;
|
||||
}
|
||||
return findIOException(t.getCause());
|
||||
}
|
||||
|
||||
public Client getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
private FeignLoadBalancer lbClient(String clientName) {
|
||||
return this.lbClientFactory.create(clientName);
|
||||
}
|
||||
|
||||
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
|
||||
|
||||
FeignOptionsClientConfig(Request.Options options) {
|
||||
setProperty(CommonClientConfigKey.ConnectTimeout,
|
||||
options.connectTimeoutMillis());
|
||||
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadProperties(String clientName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadDefaultValues() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import feign.Client;
|
||||
import feign.okhttp.OkHttpClient;
|
||||
|
||||
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.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.openfeign.clientconfig.OkHttpFeignConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(OkHttpClient.class)
|
||||
@ConditionalOnProperty("feign.okhttp.enabled")
|
||||
@Import(OkHttpFeignConfiguration.class)
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.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 org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryListener;
|
||||
import org.springframework.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.retry.backoff.NoBackOffPolicy;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
* @author Gang Li
|
||||
*/
|
||||
public class RetryableFeignLoadBalancer extends FeignLoadBalancer
|
||||
implements ServiceInstanceChooser {
|
||||
|
||||
private final 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 {
|
||||
final Request.Options options;
|
||||
if (configOverride != null) {
|
||||
RibbonProperties ribbon = RibbonProperties.from(configOverride);
|
||||
options = new Request.Options(ribbon.connectTimeout(this.connectTimeout),
|
||||
ribbon.readTimeout(this.readTimeout));
|
||||
}
|
||||
else {
|
||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||
}
|
||||
final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory
|
||||
.createRetryPolicy(this.getClientName(), this);
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
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()));
|
||||
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
|
||||
@Override
|
||||
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
|
||||
if (retryContext instanceof LoadBalancedRetryContext) {
|
||||
ServiceInstance service = ((LoadBalancedRetryContext) retryContext)
|
||||
.getServiceInstance();
|
||||
if (service != null) {
|
||||
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());
|
||||
response.close();
|
||||
throw new RibbonResponseStatusCodeException(
|
||||
RetryableFeignLoadBalancer.this.clientName, response,
|
||||
byteArray, request.getUri());
|
||||
}
|
||||
return new RibbonResponse(request.getUri(), response);
|
||||
}
|
||||
}, new LoadBalancedRecoveryCallback<RibbonResponse, Response>() {
|
||||
@Override
|
||||
protected RibbonResponse createResponse(Response response, URI uri) {
|
||||
return new RibbonResponse(uri, response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
|
||||
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(),
|
||||
requestConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceInstance choose(String serviceId) {
|
||||
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
||||
this.getLoadBalancer().chooseServer(serviceId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import feign.Response;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||
|
||||
/**
|
||||
* A {@link RetryableStatusCodeException} for {@link Response}s.
|
||||
*
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
|
||||
|
||||
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)
|
||||
.headers(response.headers()).reason(response.reason())
|
||||
.status(response.status()).request(response.request()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.support;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
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.
|
||||
*
|
||||
* @param <T> result type
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FallbackCommand<T> extends HystrixCommand<T> {
|
||||
|
||||
private T result;
|
||||
|
||||
public FallbackCommand(T result) {
|
||||
this(result, "fallback");
|
||||
}
|
||||
|
||||
protected FallbackCommand(T result, String groupname) {
|
||||
super(HystrixCommandGroupKey.Factory.asKey(groupname));
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group) {
|
||||
super(group);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
||||
int executionIsolationThreadTimeoutInMilliseconds) {
|
||||
super(group, executionIsolationThreadTimeoutInMilliseconds);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
||||
HystrixThreadPoolKey threadPool) {
|
||||
super(group, threadPool);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
||||
HystrixThreadPoolKey threadPool,
|
||||
int executionIsolationThreadTimeoutInMilliseconds) {
|
||||
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public FallbackCommand(T result, Setter setter) {
|
||||
super(setter);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T run() throws Exception {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,6 @@ 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.PageableSpringEncoder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -49,8 +48,7 @@ public class EnableFeignClientsSpringDataTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
||||
FeignAutoConfiguration.class })
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class, FeignAutoConfiguration.class })
|
||||
protected static class PlainConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||
import org.springframework.cloud.test.ClassPathExclusions;
|
||||
@@ -88,7 +87,7 @@ public class EnableFeignClientsTests {
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class })
|
||||
@Import({ FeignAutoConfiguration.class })
|
||||
protected static class PlainConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -127,10 +126,8 @@ public class FeignClientBuilderTests {
|
||||
public void forType_allFieldsSetOnBuilder() {
|
||||
// when:
|
||||
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
||||
.forType(TestFeignClient.class, "TestClient").decode404(true)
|
||||
.fallback(TestFeignClientFallback.class)
|
||||
.fallbackFactory(TestFeignClientFallbackFactory.class).path("Path/")
|
||||
.url("Url/").contextId("TestContext");
|
||||
.forType(TestFeignClient.class, "TestClient").decode404(true).url("Url/")
|
||||
.contextId("TestContext");
|
||||
|
||||
// then:
|
||||
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||
@@ -142,9 +139,7 @@ public class FeignClientBuilderTests {
|
||||
assertFactoryBeanField(builder, "url", "http://Url/");
|
||||
assertFactoryBeanField(builder, "path", "/Path");
|
||||
assertFactoryBeanField(builder, "decode404", true);
|
||||
assertFactoryBeanField(builder, "fallback", TestFeignClientFallback.class);
|
||||
assertFactoryBeanField(builder, "fallbackFactory",
|
||||
TestFeignClientFallbackFactory.class);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -167,18 +162,4 @@ public class FeignClientBuilderTests {
|
||||
|
||||
}
|
||||
|
||||
private class TestFeignClientFallback implements TestFeignClient {
|
||||
|
||||
}
|
||||
|
||||
private class TestFeignClientFallbackFactory
|
||||
implements FallbackFactory<TestFeignClient> {
|
||||
|
||||
@Override
|
||||
public TestFeignClientFallback create(Throwable throwable) {
|
||||
return new TestFeignClientFallback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.openfeign;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.ExceptionPropagationPolicy;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.QueryMapEncoder;
|
||||
import feign.Request;
|
||||
@@ -30,7 +29,6 @@ import feign.auth.BasicAuthRequestInterceptor;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
import feign.optionals.OptionalDecoder;
|
||||
import feign.querymap.BeanQueryMapEncoder;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
@@ -40,7 +38,6 @@ 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.PageableSpringEncoder;
|
||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -121,13 +118,6 @@ public class FeignClientOverrideDefaultsTests {
|
||||
.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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideRequestOptions() {
|
||||
assertThat(this.context.getInstance("foo", Request.Options.class)).isNull();
|
||||
@@ -180,8 +170,7 @@ public class FeignClientOverrideDefaultsTests {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = { FooClient.class, BarClient.class })
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
||||
FeignAutoConfiguration.class })
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class, FeignAutoConfiguration.class })
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -217,11 +206,6 @@ public class FeignClientOverrideDefaultsTests {
|
||||
return new Contract.Default();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Feign.Builder feignBuilder() {
|
||||
return HystrixFeign.builder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public QueryMapEncoder queryMapEncoder() {
|
||||
return new feign.QueryMapEncoder.Default();
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingInterceptor;
|
||||
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration;
|
||||
@@ -54,8 +53,7 @@ public class FeignCompressionTests {
|
||||
.withPropertyValues("feign.compression.response.enabled=true",
|
||||
"feign.compression.request.enabled=true",
|
||||
"feign.okhttp.enabled=false")
|
||||
.withConfiguration(AutoConfigurations.of(ArchaiusAutoConfiguration.class,
|
||||
FeignAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(FeignAutoConfiguration.class,
|
||||
FeignContentGzipEncodingAutoConfiguration.class,
|
||||
FeignAcceptGzipEncodingAutoConfiguration.class,
|
||||
HttpClientConfiguration.class, PlainConfig.class))
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -26,18 +24,10 @@ 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.test.ClassPathExclusions;
|
||||
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@@ -50,11 +40,7 @@ public class SpringRetryDisabledTests {
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
||||
.sources(RibbonAutoConfiguration.class,
|
||||
LoadBalancerAutoConfiguration.class,
|
||||
RibbonClientConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class)
|
||||
.run();
|
||||
.sources(LoadBalancerAutoConfiguration.class).run();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -66,12 +52,15 @@ public class SpringRetryDisabledTests {
|
||||
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
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);
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* 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);
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,45 +16,37 @@
|
||||
|
||||
package org.springframework.cloud.openfeign;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
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 = { LoadBalancerAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
public class SpringRetryEnabledTests implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||
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);
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* 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
|
||||
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.encoding;
|
||||
|
||||
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;
|
||||
|
||||
@@ -29,12 +25,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.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.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -75,7 +69,7 @@ public class FeignAcceptEncodingTests {
|
||||
}
|
||||
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
@@ -89,13 +83,11 @@ public class FeignAcceptEncodingTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer() {
|
||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
||||
balancer.setServersList(
|
||||
Collections.singletonList(new Server("localhost", this.port)));
|
||||
return balancer;
|
||||
}
|
||||
/*
|
||||
* @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer =
|
||||
* new BaseLoadBalancer(); balancer.setServersList( Collections.singletonList(new
|
||||
* Server("localhost", this.port))); return balancer; }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.encoding;
|
||||
|
||||
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;
|
||||
|
||||
@@ -29,12 +25,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.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.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -79,7 +73,7 @@ public class FeignContentEncodingTests {
|
||||
}
|
||||
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
@@ -93,13 +87,11 @@ public class FeignContentEncodingTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer() {
|
||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
||||
balancer.setServersList(
|
||||
Collections.singletonList(new Server("localhost", this.port)));
|
||||
return balancer;
|
||||
}
|
||||
/*
|
||||
* @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer =
|
||||
* new BaseLoadBalancer(); balancer.setServersList( Collections.singletonList(new
|
||||
* Server("localhost", this.port))); return balancer; }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.encoding;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -29,13 +24,11 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
||||
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -85,7 +78,7 @@ public class FeignPageableEncodingTests {
|
||||
}
|
||||
|
||||
@EnableFeignClients(clients = InvoiceClient.class)
|
||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app",
|
||||
exclude = { RepositoryRestMvcAutoConfiguration.class })
|
||||
@@ -101,13 +94,11 @@ public class FeignPageableEncodingTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer() {
|
||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
||||
balancer.setServersList(
|
||||
Collections.singletonList(new Server("localhost", this.port)));
|
||||
return balancer;
|
||||
}
|
||||
/*
|
||||
* @Bean public ILoadBalancer ribbonLoadBalancer() { BaseLoadBalancer balancer =
|
||||
* new BaseLoadBalancer(); balancer.setServersList( Collections.singletonList(new
|
||||
* Server("localhost", this.port))); return balancer; }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.openfeign.hateoas.app;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -29,7 +28,6 @@ import org.springframework.context.annotation.Import;
|
||||
* @author Hector Espert
|
||||
*/
|
||||
@EnableFeignClients(clients = FeignHalClient.class)
|
||||
@RibbonClient(name = "local", configuration = FeignHalRibbonConfiguration.class)
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = "org.springframework.cloud.openfeign.hateoas.app",
|
||||
exclude = RepositoryRestMvcAutoConfiguration.class)
|
||||
|
||||
@@ -16,14 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.hateoas.app;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Hector Espert
|
||||
@@ -33,12 +26,4 @@ public class FeignHalRibbonConfiguration {
|
||||
@Value("${local.server.port}")
|
||||
private int serverPort = 0;
|
||||
|
||||
@Bean
|
||||
public ILoadBalancer ribbonLoadBalancer() {
|
||||
Server server = new Server("localhost", serverPort);
|
||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
||||
balancer.setServersList(Collections.singletonList(server));
|
||||
return balancer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.ProxyUsernameController;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.TestInterceptor;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.UsernameClient;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.UsernameController;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableFeignClients(clients = UsernameClient.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public class HystrixSecurityApplication {
|
||||
|
||||
@Bean
|
||||
public CustomConcurrenyStrategy customConcurrenyStrategy() {
|
||||
return new CustomConcurrenyStrategy();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestInterceptor testInterceptor() {
|
||||
return new TestInterceptor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyUsernameController proxyUsernameController() {
|
||||
return new ProxyUsernameController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UsernameController usernameController() {
|
||||
return new UsernameController();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import com.netflix.hystrix.strategy.HystrixPlugins;
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
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.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.netflix.hystrix.security.SecurityContextConcurrencyStrategy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.hystrix.security.app.CustomConcurrenyStrategy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
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" })
|
||||
@ActiveProfiles("proxysecurity")
|
||||
public class HystrixSecurityTests {
|
||||
|
||||
@Autowired
|
||||
private CustomConcurrenyStrategy customConcurrenyStrategy;
|
||||
|
||||
@LocalServerPort
|
||||
private String serverPort;
|
||||
|
||||
// TODO: move to constants in TestAutoConfiguration
|
||||
private String username = "user";
|
||||
|
||||
private String password = "password";
|
||||
|
||||
public static HttpHeaders createBasicAuthHeader(final String username,
|
||||
final String password) {
|
||||
return new HttpHeaders() {
|
||||
private static final long serialVersionUID = 1766341693637204893L;
|
||||
|
||||
{
|
||||
String auth = username + ":" + password;
|
||||
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
|
||||
String authHeader = "Basic " + new String(encodedAuth);
|
||||
this.set("Authorization", authHeader);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@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 LocalRibbonClientConfiguration {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security.app;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
|
||||
|
||||
public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy {
|
||||
|
||||
private boolean hookCalled;
|
||||
|
||||
@Override
|
||||
public <T> Callable<T> wrapCallable(Callable<T> callable) {
|
||||
this.hookCalled = true;
|
||||
|
||||
return super.wrapCallable(callable);
|
||||
}
|
||||
|
||||
public boolean isHookCalled() {
|
||||
return this.hookCalled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security.app;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/proxy-username")
|
||||
public class ProxyUsernameController {
|
||||
|
||||
@Autowired
|
||||
private UsernameClient usernameClient;
|
||||
|
||||
@RequestMapping
|
||||
public String getUsername() {
|
||||
return this.usernameClient.getUsername();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security.app;
|
||||
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
|
||||
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) {
|
||||
template.header("username",
|
||||
SecurityContextHolder.getContext().getAuthentication().getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security.app;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@FeignClient("username")
|
||||
public interface UsernameClient {
|
||||
|
||||
@RequestMapping("/username")
|
||||
String getUsername();
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.hystrix.security.app;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/username")
|
||||
public class UsernameController {
|
||||
|
||||
@RequestMapping
|
||||
public String getUsername(@RequestHeader String username) {
|
||||
return username;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,22 +16,16 @@
|
||||
|
||||
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;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -50,9 +44,7 @@ public class FeignClientValidationTests {
|
||||
@Test
|
||||
public void testServiceIdAndValue() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class,
|
||||
NameAndServiceIdConfiguration.class);
|
||||
LoadBalancerAutoConfiguration.class, NameAndServiceIdConfiguration.class);
|
||||
assertThat(context.getBean(NameAndServiceIdConfiguration.Client.class))
|
||||
.isNotNull();
|
||||
context.close();
|
||||
@@ -63,7 +55,6 @@ public class FeignClientValidationTests {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.setAllowBeanDefinitionOverriding(false);
|
||||
context.register(LoadBalancerAutoConfiguration.class,
|
||||
RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class,
|
||||
DuplicatedFeignClientNamesConfiguration.class);
|
||||
context.refresh();
|
||||
assertThat(
|
||||
@@ -81,47 +72,6 @@ public class FeignClientValidationTests {
|
||||
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(proxyBeanMethods = false)
|
||||
@Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class })
|
||||
@EnableFeignClients(clients = NameAndServiceIdConfiguration.Client.class)
|
||||
@@ -177,123 +127,4 @@ public class FeignClientValidationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = MissingFallbackConfiguration.Client.class)
|
||||
protected static class MissingFallbackConfiguration {
|
||||
|
||||
@Bean
|
||||
public Feign.Builder feignBuilder() {
|
||||
return HystrixFeign.builder();
|
||||
}
|
||||
|
||||
@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(proxyBeanMethods = false)
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = WrongFallbackTypeConfiguration.Client.class)
|
||||
protected static class WrongFallbackTypeConfiguration {
|
||||
|
||||
@Bean
|
||||
Dummy dummy() {
|
||||
return new 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();
|
||||
|
||||
}
|
||||
|
||||
class Dummy {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@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> {
|
||||
|
||||
@Override
|
||||
public Client create(Throwable cause) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(FeignAutoConfiguration.class)
|
||||
@EnableFeignClients(clients = WrongFallbackFactoryTypeConfiguration.Client.class)
|
||||
protected static class WrongFallbackFactoryTypeConfiguration {
|
||||
|
||||
@Bean
|
||||
Dummy dummy() {
|
||||
return new 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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,20 +18,6 @@ package org.springframework.cloud.openfeign.loadbalancer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import feign.okhttp.OkHttpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration;
|
||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -41,54 +27,46 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
class FeignLoadBalancerAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void shouldInstantiateDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
|
||||
ConfigurableApplicationContext context = initContext(
|
||||
"spring.cloud.loadbalancer.ribbon.enabled=false",
|
||||
"feign.httpclient.enabled=false");
|
||||
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
|
||||
assertLoadBalanced(context, Client.Default.class);
|
||||
assertThatBeanNotPresent(context, LoadBalancerFeignClient.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateHttpFeignClientWhenEnabled() {
|
||||
ConfigurableApplicationContext context = initContext(
|
||||
"spring.cloud.loadbalancer.ribbon.enabled=false");
|
||||
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
|
||||
assertLoadBalanced(context, ApacheHttpClient.class);
|
||||
assertThatBeanNotPresent(context, LoadBalancerFeignClient.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInstantiateOkHttpFeignClientWhenEnabled() {
|
||||
ConfigurableApplicationContext context = initContext(
|
||||
"spring.cloud.loadbalancer.ribbon.enabled=false",
|
||||
"feign.httpclient.enabled=false", "feign.okhttp.enabled=true");
|
||||
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
|
||||
assertLoadBalanced(context, OkHttpClient.class);
|
||||
assertThatBeanNotPresent(context, LoadBalancerFeignClient.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotProcessLoadBalancerConfigurationWhenRibbonEnabled() {
|
||||
ConfigurableApplicationContext context = initContext(
|
||||
"spring.cloud.loadbalancer.ribbon.enabled=true");
|
||||
assertThatOneBeanPresent(context, LoadBalancerFeignClient.class);
|
||||
assertThatBeanNotPresent(context, BlockingLoadBalancerClient.class);
|
||||
assertThatBeanNotPresent(context, FeignBlockingLoadBalancerClient.class);
|
||||
}
|
||||
|
||||
private ConfigurableApplicationContext initContext(String... properties) {
|
||||
return new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
||||
.properties(properties)
|
||||
.sources(HttpClientConfiguration.class, RibbonAutoConfiguration.class,
|
||||
LoadBalancerAutoConfiguration.class,
|
||||
BlockingLoadBalancerClientAutoConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class,
|
||||
FeignLoadBalancerAutoConfiguration.class)
|
||||
.run();
|
||||
}
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* @Test void
|
||||
* shouldInstantiateDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
|
||||
* ConfigurableApplicationContext context = initContext(
|
||||
* "spring.cloud.loadbalancer.ribbon.enabled=false",
|
||||
* "feign.httpclient.enabled=false"); assertThatOneBeanPresent(context,
|
||||
* BlockingLoadBalancerClient.class); assertLoadBalanced(context,
|
||||
* Client.Default.class); assertThatBeanNotPresent(context,
|
||||
* LoadBalancerFeignClient.class); }
|
||||
*
|
||||
* @Test void shouldInstantiateHttpFeignClientWhenEnabled() {
|
||||
* ConfigurableApplicationContext context = initContext(
|
||||
* "spring.cloud.loadbalancer.ribbon.enabled=false");
|
||||
* assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
|
||||
* assertLoadBalanced(context, ApacheHttpClient.class);
|
||||
* assertThatBeanNotPresent(context, LoadBalancerFeignClient.class); }
|
||||
*
|
||||
* @Test void shouldInstantiateOkHttpFeignClientWhenEnabled() {
|
||||
* ConfigurableApplicationContext context = initContext(
|
||||
* "spring.cloud.loadbalancer.ribbon.enabled=false", "feign.httpclient.enabled=false",
|
||||
* "feign.okhttp.enabled=true"); assertThatOneBeanPresent(context,
|
||||
* BlockingLoadBalancerClient.class); assertLoadBalanced(context, OkHttpClient.class);
|
||||
* assertThatBeanNotPresent(context, LoadBalancerFeignClient.class); }
|
||||
*
|
||||
* @Test void shouldNotProcessLoadBalancerConfigurationWhenRibbonEnabled() {
|
||||
* ConfigurableApplicationContext context = initContext(
|
||||
* "spring.cloud.loadbalancer.ribbon.enabled=true"); assertThatOneBeanPresent(context,
|
||||
* LoadBalancerFeignClient.class); assertThatBeanNotPresent(context,
|
||||
* BlockingLoadBalancerClient.class); assertThatBeanNotPresent(context,
|
||||
* FeignBlockingLoadBalancerClient.class); }
|
||||
*
|
||||
* private ConfigurableApplicationContext initContext(String... properties) { return
|
||||
* new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties)
|
||||
* .sources(HttpClientConfiguration.class, RibbonAutoConfiguration.class,
|
||||
* LoadBalancerAutoConfiguration.class,
|
||||
* BlockingLoadBalancerClientAutoConfiguration.class,
|
||||
* FeignRibbonClientAutoConfiguration.class, FeignLoadBalancerAutoConfiguration.class)
|
||||
* .run(); }
|
||||
*/
|
||||
|
||||
private void assertThatOneBeanPresent(ConfigurableApplicationContext context,
|
||||
Class<?> beanClass) {
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
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.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class CachingSpringLoadBalancerFactoryTests {
|
||||
|
||||
@Mock
|
||||
private SpringClientFactory delegate;
|
||||
|
||||
@Mock
|
||||
private RibbonLoadBalancedRetryFactory loadBalancedRetryFactory;
|
||||
|
||||
private CachingSpringLoadBalancerFactory factory;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
IClientConfig config = new DefaultClientConfigImpl();
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 500);
|
||||
|
||||
when(this.delegate.getClientConfig("client1")).thenReturn(config);
|
||||
when(this.delegate.getClientConfig("client2")).thenReturn(config);
|
||||
|
||||
this.factory = new CachingSpringLoadBalancerFactory(this.delegate,
|
||||
this.loadBalancedRetryFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delegateCreatesWhenMissing() {
|
||||
FeignLoadBalancer client = this.factory.create("client1");
|
||||
assertThat(client).as("client was null").isNotNull();
|
||||
|
||||
verify(this.delegate, times(1)).getClientConfig("client1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWorks() {
|
||||
FeignLoadBalancer client = this.factory.create("client2");
|
||||
assertThat(client).as("client was null").isNotNull();
|
||||
|
||||
client = this.factory.create("client2");
|
||||
assertThat(client).as("client was null").isNotNull();
|
||||
|
||||
verify(this.delegate, times(1)).getClientConfig("client2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delegateCreatesWithNoRetry() {
|
||||
IClientConfig config = new DefaultClientConfigImpl();
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 500);
|
||||
when(this.delegate.getClientConfig("retry")).thenReturn(config);
|
||||
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(
|
||||
this.delegate);
|
||||
FeignLoadBalancer client = this.factory.create("retry");
|
||||
assertThat(client).as("client was null").isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delegateCreatesWithRetry() {
|
||||
IClientConfig config = new DefaultClientConfigImpl();
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 500);
|
||||
when(this.delegate.getClientConfig("retry")).thenReturn(config);
|
||||
CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(
|
||||
this.delegate, this.loadBalancedRetryFactory);
|
||||
FeignLoadBalancer client = this.factory.create("retry");
|
||||
assertThat(client).as("client was null").isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.RoundRobinRule;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonRequest;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.RibbonResponse;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.IsSecure;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetriesNextServer;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOperations;
|
||||
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.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class FeignLoadBalancerTests {
|
||||
|
||||
@Mock
|
||||
private Client delegate;
|
||||
|
||||
@Mock
|
||||
private ILoadBalancer lb;
|
||||
|
||||
@Mock
|
||||
private IClientConfig config;
|
||||
|
||||
private FeignLoadBalancer feignLoadBalancer;
|
||||
|
||||
private ServerIntrospector inspector = new DefaultServerIntrospector();
|
||||
|
||||
private Integer defaultConnectTimeout = 10000;
|
||||
|
||||
private Integer defaultReadTimeout = 10000;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(this.config.get(MaxAutoRetries, DEFAULT_MAX_AUTO_RETRIES)).thenReturn(1);
|
||||
when(this.config.get(MaxAutoRetriesNextServer,
|
||||
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(1);
|
||||
when(this.config.get(OkToRetryOnAllOperations, eq(anyBoolean())))
|
||||
.thenReturn(true);
|
||||
when(this.config.get(ConnectTimeout)).thenReturn(this.defaultConnectTimeout);
|
||||
when(this.config.get(ReadTimeout)).thenReturn(this.defaultReadTimeout);
|
||||
when(this.config.get(OkToRetryOnAllOperations, false)).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUriInsecure() throws Exception {
|
||||
when(this.config.get(IsSecure)).thenReturn(false);
|
||||
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Request request = new RequestTemplate().method(GET).target("https://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();
|
||||
when(this.delegate.execute(any(Request.class), any(Options.class)))
|
||||
.thenReturn(response);
|
||||
|
||||
RibbonResponse resp = this.feignLoadBalancer.execute(ribbonRequest, null);
|
||||
|
||||
assertThat(resp.getRequestedURI()).isEqualTo(new URI("https://foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecureUriFromClientConfig() throws Exception {
|
||||
when(this.config.get(IsSecure)).thenReturn(true);
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Server server = new Server("foo", 7777);
|
||||
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
|
||||
new URI("https://foo/"));
|
||||
assertThat(uri).isEqualTo(new URI("https://foo:7777/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsecureUriFromInsecureClientConfigToSecureServerIntrospector()
|
||||
throws Exception {
|
||||
when(this.config.get(IsSecure)).thenReturn(false);
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
new ServerIntrospector() {
|
||||
@Override
|
||||
public boolean isSecure(Server server) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata(Server server) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Server server = new Server("foo", 7777);
|
||||
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
|
||||
new URI("https://foo/"));
|
||||
assertThat(uri).isEqualTo(new URI("https://foo:7777/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecureUriFromClientConfigOverride() throws Exception {
|
||||
this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config,
|
||||
this.inspector);
|
||||
Server server = Mockito.mock(Server.class);
|
||||
when(server.getPort()).thenReturn(443);
|
||||
when(server.getHost()).thenReturn("foo");
|
||||
URI uri = this.feignLoadBalancer.reconstructURIWithServer(server,
|
||||
new URI("https://bar/"));
|
||||
assertThat(uri).isEqualTo(new URI("https://foo:443/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRibbonRequestURLEncode() throws Exception {
|
||||
String url = "https://foo/?name=%7bcookie"; // name={cookie
|
||||
Request request = Request.create(GET, url, new HashMap<>(), null, null, null);
|
||||
|
||||
assertThat(request.url()).isEqualTo(url);
|
||||
|
||||
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request,
|
||||
new URI(request.url()));
|
||||
|
||||
Request cloneRequest = ribbonRequest.toRequest();
|
||||
|
||||
assertThat(cloneRequest.url()).isEqualTo(url);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideFeignLoadBalancer() throws Exception {
|
||||
when(this.config.get(IsSecure)).thenReturn(false);
|
||||
Server server1 = new Server("foo", 6666);
|
||||
Server server2 = new Server("foo", 7777);
|
||||
BaseLoadBalancer baseLoadBalancer = new BaseLoadBalancer();
|
||||
baseLoadBalancer.setRule(new RoundRobinRule() {
|
||||
@Override
|
||||
public Server choose(Object loadBalancerKey) {
|
||||
return loadBalancerKey == null ? server2 : server1;
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
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()).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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
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.cloud.netflix.ribbon.RibbonClients;
|
||||
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;
|
||||
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 org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
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 = 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 {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Autowired
|
||||
private TestClient1 testClient1;
|
||||
|
||||
@Autowired
|
||||
private TestClient2 testClient2;
|
||||
|
||||
@Autowired
|
||||
private TestClient3 testClient3;
|
||||
|
||||
@Autowired
|
||||
private TestClient4 testClient4;
|
||||
|
||||
@Autowired
|
||||
private TestClient5 testClient5;
|
||||
|
||||
@Test
|
||||
public void pathWithLeadingButNotTrailingSlash() {
|
||||
testClientPath(this.testClient1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathWithoutLeadingAndTrailingSlash() {
|
||||
testClientPath(this.testClient2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathWithoutLeadingButTrailingSlash() {
|
||||
testClientPath(this.testClient3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathWithLeadingAndTrailingSlash() {
|
||||
testClientPath(this.testClient4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathWithPlaceholder() {
|
||||
testClientPath(this.testClient5);
|
||||
}
|
||||
|
||||
private void testClientPath(TestClient testClient) {
|
||||
Hello hello = testClient.getHello();
|
||||
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(proxyBeanMethods = false)
|
||||
@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(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class LocalRibbonClientConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
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.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;
|
||||
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 org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
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 = 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" })
|
||||
@DirtiesContext
|
||||
public class FeignRibbonClientRetryTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@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(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = TestClient.class)
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
public static class Application {
|
||||
|
||||
private AtomicInteger retries = new AtomicInteger(1);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||
public Hello getHello() {
|
||||
return new Hello("hello world 1");
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/retryme")
|
||||
public int retryMe() {
|
||||
return this.retries.getAndIncrement();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load balancer with fixed server list for "local" pointing to localhost
|
||||
// some bogus servers are thrown in to test retry
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class LocalRibbonClientConfiguration {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.LoadBalancerStats;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerStats;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Request.Options;
|
||||
import feign.RequestTemplate;
|
||||
import org.hamcrest.CustomMatcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
|
||||
import static feign.Request.HttpMethod.GET;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class FeignRibbonClientTests {
|
||||
|
||||
private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class);
|
||||
|
||||
private Client delegate = mock(Client.class);
|
||||
|
||||
private SpringClientFactory factory = new SpringClientFactory() {
|
||||
@Override
|
||||
public IClientConfig getClientConfig(String name) {
|
||||
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
|
||||
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
|
||||
config.set(CommonClientConfigKey.ReadTimeout, 500);
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <C> C getInstance(String name, Class<C> type) {
|
||||
if (type.isAssignableFrom(ServerIntrospector.class)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
C instance = (C) new DefaultServerIntrospector();
|
||||
return instance;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILoadBalancer getLoadBalancer(String name) {
|
||||
return FeignRibbonClientTests.this.loadBalancer;
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteRequestIsSentAtRoot() throws Exception {
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteRequestIsSent() throws Exception {
|
||||
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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyCleanUrl() throws Exception {
|
||||
Request request = new RequestTemplate().method(GET)
|
||||
.target("https://tp/abc/bcd.json").resolve(new HashMap<>()).request();
|
||||
this.client.execute(request, new Options());
|
||||
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/abc/bcd.json");
|
||||
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();
|
||||
this.client.execute(request, new Options());
|
||||
RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/");
|
||||
verify(this.delegate).execute(argThat(matcher), any(Options.class));
|
||||
}
|
||||
|
||||
private final static class RequestMatcher extends CustomMatcher<Request> {
|
||||
|
||||
private String url;
|
||||
|
||||
private RequestMatcher(String url) {
|
||||
super("request has URI: " + url);
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object item) {
|
||||
Request request = (Request) item;
|
||||
return request.url().equals(this.url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.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.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 = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
properties = { "debug=true", "feign.httpclient.disableSslValidation=true" })
|
||||
@DirtiesContext
|
||||
public class FeignRibbonHttpClientConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
HttpClientConnectionManager connectionManager;
|
||||
|
||||
@Test
|
||||
public void disableSslTest() throws Exception {
|
||||
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 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) {
|
||||
Field field = ReflectionUtils.findField(target.getClass(), name);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Object value = ReflectionUtils.getField(field, target);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
static class FeignRibbonHttpClientConfigurationTestsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
|
||||
.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
|
||||
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.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" })
|
||||
@DirtiesContext
|
||||
public class FeignRibbonOkHttpClientConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
OkHttpClient httpClient;
|
||||
|
||||
@Test
|
||||
public void disableSslTest() throws Exception {
|
||||
HostnameVerifier hostnameVerifier = (HostnameVerifier) this
|
||||
.getField(this.httpClient, "hostnameVerifier");
|
||||
assertThat(
|
||||
OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
protected <T> Object getField(Object target, String name) {
|
||||
Field field = ReflectionUtils.findField(target.getClass(), name);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Object value = ReflectionUtils.getField(field, target);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
static class FeignRibbonOkHttpClientConfigurationTestsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class)
|
||||
.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
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;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = LoadBalancerFeignClientOverrideTests.TestConfiguration.class,
|
||||
webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
value = { "spring.application.name=loadBalancerFeignClientTests",
|
||||
"feign.httpclient.enabled=false", "feign.okhttp.enabled=false" })
|
||||
@DirtiesContext
|
||||
public class LoadBalancerFeignClientOverrideTests {
|
||||
|
||||
@Autowired
|
||||
private FeignContext context;
|
||||
|
||||
@Test
|
||||
public void overrideRequestOptions() {
|
||||
// specific ribbon 'bar' configuration via spring bean
|
||||
Request.Options barOptions = this.context.getInstance("bar",
|
||||
Request.Options.class);
|
||||
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);
|
||||
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);
|
||||
assertThat(bazOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS);
|
||||
assertOptions(bazOptions, "baz", 3001, 60001);
|
||||
}
|
||||
|
||||
void assertOptions(Request.Options options, String name, int expectedConnect,
|
||||
int expectedRead) {
|
||||
LoadBalancerFeignClient client = this.context.getInstance(name,
|
||||
LoadBalancerFeignClient.class);
|
||||
IClientConfig config = client.getClientConfig(options, name);
|
||||
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(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = { FooClient.class, BarClient.class, BazClient.class })
|
||||
@EnableAutoConfiguration
|
||||
protected static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
public static class FooConfiguration {
|
||||
|
||||
}
|
||||
|
||||
public static class BarConfiguration {
|
||||
|
||||
@Bean
|
||||
public Request.Options feignRequestOptions() {
|
||||
return new Request.Options(1, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,702 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
||||
import com.netflix.client.RequestSpecificRetryHandler;
|
||||
import com.netflix.client.config.CommonClientConfigKey;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
||||
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryPolicy;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerContext;
|
||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryListener;
|
||||
import org.springframework.retry.TerminatedRetryException;
|
||||
import org.springframework.retry.backoff.BackOffContext;
|
||||
import org.springframework.retry.backoff.BackOffInterruptedException;
|
||||
import org.springframework.retry.backoff.BackOffPolicy;
|
||||
|
||||
import static com.netflix.client.config.CommonClientConfigKey.ConnectTimeout;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetries;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.MaxAutoRetriesNextServer;
|
||||
import static com.netflix.client.config.CommonClientConfigKey.OkToRetryOnAllOperations;
|
||||
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 java.nio.charset.StandardCharsets.UTF_8;
|
||||
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;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Gang Li
|
||||
* @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
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(this.config.get(MaxAutoRetries, DEFAULT_MAX_AUTO_RETRIES)).thenReturn(1);
|
||||
when(this.config.get(MaxAutoRetriesNextServer,
|
||||
DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(1);
|
||||
when(this.config.get(OkToRetryOnAllOperations, eq(anyBoolean())))
|
||||
.thenReturn(true);
|
||||
when(this.config.get(ConnectTimeout)).thenReturn(this.defaultConnectTimeout);
|
||||
when(this.config.get(ReadTimeout)).thenReturn(this.defaultReadTimeout);
|
||||
when(this.config.get(OkToRetryOnAllOperations, false)).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeNoFailure() throws Exception {
|
||||
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(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);
|
||||
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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);
|
||||
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
|
||||
verify(client, times(1)).execute(any(Request.class), any(Request.Options.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeNeverRetry() throws Exception {
|
||||
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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 BackOffPolicy createBackOffPolicy(String service) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
try {
|
||||
feignLb.execute(request, null);
|
||||
}
|
||||
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(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(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) {
|
||||
@Override
|
||||
public BackOffPolicy createBackOffPolicy(String service) {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
Request feignRequest = Request.create(GET, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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(this.lb,
|
||||
config, this.inspector, loadBalancedRetryFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
assertThat(backOffPolicy.getCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeRetryOnStatusCode() throws Exception {
|
||||
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(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, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://foo"));
|
||||
Response response = Response.builder().request(feignRequest).status(200)
|
||||
.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(200);
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
assertThat(backOffPolicy.getCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@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(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, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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(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, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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(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, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server chooseServer(Object o) {
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markServerDown(Server server) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Server> getServerList(boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Server> getReachableServers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Server> getAllServers() {
|
||||
return null;
|
||||
}
|
||||
}, this.config, this.inspector, loadBalancedRetryFactory);
|
||||
ServiceInstance serviceInstance = feignLb.choose("foo");
|
||||
assertThat(serviceInstance.getHost()).isEqualTo("foo");
|
||||
assertThat(serviceInstance.getPort()).isEqualTo(80);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retryListenerTest() throws Exception {
|
||||
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(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) {
|
||||
@Override
|
||||
public RetryListener[] createRetryListeners(String service) {
|
||||
return new RetryListener[] { myRetryListener };
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackOffPolicy createBackOffPolicy(String service) {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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, loadBalancedRetryFactory);
|
||||
FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null);
|
||||
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
assertThat(backOffPolicy.getCount()).isEqualTo(1);
|
||||
assertThat(myRetryListener.getOnError()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test(expected = TerminatedRetryException.class)
|
||||
public void retryListenerTestNoRetry() throws Exception {
|
||||
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(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) {
|
||||
@Override
|
||||
public RetryListener[] createRetryListeners(String service) {
|
||||
return new RetryListener[] { myRetryListenerNotRetry };
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackOffPolicy createBackOffPolicy(String service) {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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(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(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) {
|
||||
@Override
|
||||
public BackOffPolicy createBackOffPolicy(String service) {
|
||||
return backOffPolicy;
|
||||
}
|
||||
};
|
||||
Request feignRequest = Request.create(GET, "https://listener", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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);
|
||||
assertThat(ribbonResponse.toResponse().status()).isEqualTo(200);
|
||||
verify(client, times(2)).execute(any(Request.class), any(Request.Options.class));
|
||||
assertThat(backOffPolicy.getCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeRetryFail() throws Exception {
|
||||
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(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, "https://foo", new HashMap<>(),
|
||||
new byte[] {}, UTF_8);
|
||||
Client client = mock(Client.class);
|
||||
FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(
|
||||
client, feignRequest, new URI("https://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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRepeatable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream asInputStream() throws IOException {
|
||||
return new ByteArrayInputStream("test".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader asReader() throws IOException {
|
||||
return new InputStreamReader(asInputStream(), UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader asReader(Charset charset) throws IOException {
|
||||
return new InputStreamReader(asInputStream(), charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}).build();
|
||||
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));
|
||||
assertThat(backOffPolicy.getCount()).isEqualTo(1);
|
||||
InputStream inputStream = ribbonResponse.toResponse().body().asInputStream();
|
||||
byte[] buf = new byte[100];
|
||||
int read = inputStream.read(buf);
|
||||
assertThat(new String(buf, 0, read)).isEqualTo("test");
|
||||
}
|
||||
|
||||
class MyBackOffPolicy implements BackOffPolicy {
|
||||
|
||||
private int count = 0;
|
||||
|
||||
@Override
|
||||
public BackOffContext start(RetryContext retryContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backOff(BackOffContext backOffContext)
|
||||
throws BackOffInterruptedException {
|
||||
this.count++;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MyRetryListener implements RetryListener {
|
||||
|
||||
private int onError = 0;
|
||||
|
||||
@Override
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, E extends Throwable> void onError(RetryContext context,
|
||||
RetryCallback<T, E> callback, Throwable throwable) {
|
||||
this.onError++;
|
||||
}
|
||||
|
||||
public int getOnError() {
|
||||
return this.onError;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MyRetryListenerNotRetry implements RetryListener {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, E extends Throwable> void onError(RetryContext context,
|
||||
RetryCallback<T, E> callback, Throwable throwable) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.openfeign.ribbon;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static feign.Request.HttpMethod.GET;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RibbonResponseStatusCodeExceptionTest {
|
||||
|
||||
@Test
|
||||
public void getResponse() throws Exception {
|
||||
Map<String, Collection<String>> headers = new HashMap<>();
|
||||
List<String> fooValues = new ArrayList<>();
|
||||
fooValues.add("bar");
|
||||
headers.put("foo", fooValues);
|
||||
Request request = Request.create(GET, "https://service.com", new HashMap<>(),
|
||||
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()));
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,11 +20,8 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import feign.Client;
|
||||
import feign.httpclient.ApacheHttpClient;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.StatusLine;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
@@ -35,7 +32,6 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockingDetails;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -48,7 +44,6 @@ import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientConne
|
||||
import org.springframework.cloud.commons.httpclient.DefaultApacheHttpClientFactory;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -59,7 +54,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockingDetails;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -76,8 +70,9 @@ public class ApacheHttpClientConfigurationTests {
|
||||
@Autowired
|
||||
ApacheHttpClientFactory httpClientFactory;
|
||||
|
||||
@Autowired
|
||||
LoadBalancerFeignClient feignClient;
|
||||
/*
|
||||
* @Autowired LoadBalancerFeignClient feignClient;
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testFactories() {
|
||||
@@ -92,12 +87,15 @@ public class ApacheHttpClientConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testHttpClientWithFeign() {
|
||||
Client delegate = this.feignClient.getDelegate();
|
||||
assertThat(ApacheHttpClient.class.isInstance(delegate)).isTrue();
|
||||
ApacheHttpClient apacheHttpClient = (ApacheHttpClient) delegate;
|
||||
HttpClient httpClient = getField(apacheHttpClient, "client");
|
||||
MockingDetails httpClientDetails = mockingDetails(httpClient);
|
||||
assertThat(httpClientDetails.isMock()).isTrue();
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* Client delegate = this.feignClient.getDelegate();
|
||||
* assertThat(ApacheHttpClient.class.isInstance(delegate)).isTrue();
|
||||
* ApacheHttpClient apacheHttpClient = (ApacheHttpClient) delegate; HttpClient
|
||||
* httpClient = getField(apacheHttpClient, "client"); MockingDetails
|
||||
* httpClientDetails = mockingDetails(httpClient);
|
||||
* assertThat(httpClientDetails.isMock()).isTrue();
|
||||
*/
|
||||
}
|
||||
|
||||
protected <T> T getField(Object target, String name) {
|
||||
|
||||
@@ -19,12 +19,10 @@ package org.springframework.cloud.openfeign.test;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import feign.Client;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockingDetails;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
@@ -35,7 +33,6 @@ import org.springframework.cloud.commons.httpclient.DefaultOkHttpClientFactory;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -43,7 +40,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockingDetails;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
@@ -63,8 +59,9 @@ public class OkHttpClientConfigurationTests {
|
||||
@Autowired
|
||||
OkHttpClientConnectionPoolFactory connectionPoolFactory;
|
||||
|
||||
@Autowired
|
||||
LoadBalancerFeignClient feignClient;
|
||||
/*
|
||||
* @Autowired LoadBalancerFeignClient feignClient;
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testFactories() {
|
||||
@@ -79,12 +76,15 @@ public class OkHttpClientConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testHttpClientWithFeign() {
|
||||
Client delegate = this.feignClient.getDelegate();
|
||||
assertThat(feign.okhttp.OkHttpClient.class.isInstance(delegate)).isTrue();
|
||||
feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient) delegate;
|
||||
OkHttpClient httpClient = getField(okHttpClient, "delegate");
|
||||
MockingDetails httpClientDetails = mockingDetails(httpClient);
|
||||
assertThat(httpClientDetails.isMock()).isTrue();
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* Client delegate = this.feignClient.getDelegate();
|
||||
* assertThat(feign.okhttp.OkHttpClient.class.isInstance(delegate)).isTrue();
|
||||
* feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient) delegate;
|
||||
* OkHttpClient httpClient = getField(okHttpClient, "delegate"); MockingDetails
|
||||
* httpClientDetails = mockingDetails(httpClient);
|
||||
* assertThat(httpClientDetails.isMock()).isTrue();
|
||||
*/
|
||||
}
|
||||
|
||||
protected <T> T getField(Object target, String name) {
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Logger;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -28,8 +26,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.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.context.annotation.Bean;
|
||||
@@ -104,7 +100,8 @@ public class FeignClientNotPrimaryTests {
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class },
|
||||
defaultConfiguration = TestDefaultFeignConfig.class)
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "localapp", configuration =
|
||||
// LocalRibbonClientConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
@Bean
|
||||
@@ -167,10 +164,10 @@ public class FeignClientNotPrimaryTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.text.ParseException;
|
||||
import java.time.LocalDate;
|
||||
@@ -28,43 +27,24 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
import com.netflix.hystrix.HystrixCommandKey;
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import feign.hystrix.SetterFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import rx.Observable;
|
||||
import rx.Single;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
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.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.FeignFormatterRegistrar;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.openfeign.support.FallbackCommand;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -110,16 +90,6 @@ public class FeignClientTests {
|
||||
|
||||
public static final String MYHEADER2 = "myheader2";
|
||||
|
||||
@Autowired
|
||||
HystrixClient hystrixClient;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("localapp3FeignClient")
|
||||
HystrixClient namedHystrixClient;
|
||||
|
||||
@Autowired
|
||||
HystrixSetterFactoryClient hystrixSetterFactoryClient;
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@@ -135,15 +105,6 @@ public class FeignClientTests {
|
||||
@Autowired
|
||||
private Client feignClient;
|
||||
|
||||
@Autowired
|
||||
private HystrixClientWithFallBackFactory hystrixClientWithFallBackFactory;
|
||||
|
||||
@Autowired
|
||||
private InvalidTypeHystrixClientWithFallBackFactory invalidTypeHystrixClientWithFallBackFactory;
|
||||
|
||||
@Autowired
|
||||
private NullHystrixClientWithFallBackFactory nullHystrixClientWithFallBackFactory;
|
||||
|
||||
private static ArrayList<Hello> getHelloList() {
|
||||
ArrayList<Hello> hellos = new ArrayList<>();
|
||||
hellos.add(new Hello(HELLO_WORLD_1));
|
||||
@@ -205,13 +166,13 @@ public class FeignClientTests {
|
||||
assertThat(header).as("header was wrong").isEqualTo("myPlaceholderHeaderValue");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate).isInstanceOf(Client.Default.class);
|
||||
}
|
||||
/*
|
||||
* @Test FIXME 3.0.0 public void testFeignClientType() throws IllegalAccessException {
|
||||
* assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
* LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; Client
|
||||
* delegate = client.getDelegate();
|
||||
* assertThat(delegate).isInstanceOf(Client.Default.class); }
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
@@ -240,23 +201,6 @@ public class FeignClientTests {
|
||||
assertThat(params).as("params not converted correctly").isEqualTo(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixCommand() throws NoSuchMethodException {
|
||||
HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix();
|
||||
assertThat(command).as("command was null").isNotNull();
|
||||
assertThat(command.getCommandGroup().name()).as(
|
||||
"Hystrix command group name should match the name of the feign client")
|
||||
.isEqualTo("localapp");
|
||||
String configKey = Feign.configKey(TestClient.class,
|
||||
TestClient.class.getMethod("getHellosHystrix", (Class<?>[]) null));
|
||||
assertThat(command.getCommandKey().name())
|
||||
.as("Hystrix command key name should match the feign config key")
|
||||
.isEqualTo(configKey);
|
||||
List<Hello> hellos = command.execute();
|
||||
assertThat(hellos).as("hellos was null").isNotNull();
|
||||
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingle() {
|
||||
Single<Hello> single = this.testClient.getHelloSingle();
|
||||
@@ -331,95 +275,11 @@ public class FeignClientTests {
|
||||
assertThat(this.testClient.getToString(args)).isEqualTo(expectedResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackWorks() {
|
||||
Hello hello = this.hystrixClient.fail();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("message was wrong").isEqualTo("fallback");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackSingle() {
|
||||
Single<Hello> single = this.hystrixClient.failSingle();
|
||||
assertThat(single).as("single was null").isNotNull();
|
||||
Hello hello = single.toBlocking().value();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("message was wrong")
|
||||
.isEqualTo("fallbacksingle");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackCommand() {
|
||||
HystrixCommand<Hello> command = this.hystrixClient.failCommand();
|
||||
assertThat(command).as("command was null").isNotNull();
|
||||
Hello hello = command.execute();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("message was wrong")
|
||||
.isEqualTo("fallbackcommand");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackObservable() {
|
||||
Observable<Hello> observable = this.hystrixClient.failObservable();
|
||||
assertThat(observable).as("observable was null").isNotNull();
|
||||
Hello hello = observable.toBlocking().first();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("message was wrong")
|
||||
.isEqualTo("fallbackobservable");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixFallbackFuture() throws Exception {
|
||||
Future<Hello> future = this.hystrixClient.failFuture();
|
||||
assertThat(future).as("future was null").isNotNull();
|
||||
Hello hello = future.get(1, TimeUnit.SECONDS);
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("message was wrong")
|
||||
.isEqualTo("fallbackfuture");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixClientWithFallBackFactory() throws Exception {
|
||||
Hello hello = this.hystrixClientWithFallBackFactory.fail();
|
||||
assertThat(hello).as("hello was null").isNotNull();
|
||||
assertThat(hello.getMessage()).as("hello#message was null").isNotNull();
|
||||
assertThat(hello.getMessage().contains("500")).as(
|
||||
"hello#message did not contain the cause (status code) of the fallback invocation")
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test(expected = HystrixRuntimeException.class)
|
||||
public void testInvalidTypeHystrixFallbackFactory() throws Exception {
|
||||
this.invalidTypeHystrixClientWithFallBackFactory.fail();
|
||||
}
|
||||
|
||||
@Test(expected = HystrixRuntimeException.class)
|
||||
public void testNullHystrixFallbackFactory() throws Exception {
|
||||
this.nullHystrixClientWithFallBackFactory.fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedFeignClientWorks() {
|
||||
assertThat(this.namedHystrixClient).as("namedHystrixClient was null").isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHystrixSetterFactory() {
|
||||
HystrixCommand<List<Hello>> command = this.hystrixSetterFactoryClient
|
||||
.getHellosHystrix();
|
||||
assertThat(command).as("command was null").isNotNull();
|
||||
String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX;
|
||||
assertThat(command.getCommandGroup().name()).as(
|
||||
"Hystrix command group name should match the name of the feign client with a prefix of "
|
||||
+ setterPrefix)
|
||||
.isEqualTo(setterPrefix + "localapp5");
|
||||
assertThat(command.getCommandKey().name()).as(
|
||||
"Hystrix command key name should match the request method (space) request path with a prefix of "
|
||||
+ setterPrefix)
|
||||
.isEqualTo(setterPrefix + "GET /hellos");
|
||||
List<Hello> hellos = command.execute();
|
||||
assertThat(hellos).as("hellos was null").isNotNull();
|
||||
assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos);
|
||||
// FIXME: 3.0.0
|
||||
// assertThat(this.namedHystrixClient).as("namedHystrixClient was
|
||||
// null").isNotNull();
|
||||
}
|
||||
|
||||
protected enum Arg {
|
||||
@@ -469,9 +329,6 @@ public class FeignClientTests {
|
||||
List<LocalDate> getFormattedParams(@RequestParam("params") @DateTimeFormat(
|
||||
pattern = "dd-MM-yyyy") List<LocalDate> params);
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
|
||||
HystrixCommand<List<Hello>> getHellosHystrix();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/noContent")
|
||||
ResponseEntity<Void> noContent();
|
||||
|
||||
@@ -517,61 +374,6 @@ public class FeignClientTests {
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(name = "localapp3", fallback = HystrixClientFallback.class)
|
||||
protected interface HystrixClient {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Single<Hello> failSingle();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Hello fail();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
HystrixCommand<Hello> failCommand();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Observable<Hello> failObservable();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Future<Hello> failFuture();
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(name = "localapp4", fallbackFactory = HystrixClientFallbackFactory.class)
|
||||
protected interface HystrixClientWithFallBackFactory {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Hello fail();
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(name = "localapp6",
|
||||
fallbackFactory = InvalidTypeHystrixClientFallbackFactory.class)
|
||||
protected interface InvalidTypeHystrixClientWithFallBackFactory {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Hello fail();
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(name = "localapp7",
|
||||
fallbackFactory = NullHystrixClientFallbackFactory.class)
|
||||
protected interface NullHystrixClientWithFallBackFactory {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/fail")
|
||||
Hello fail();
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(name = "localapp5",
|
||||
configuration = TestHystrixSetterFactoryClientConfig.class)
|
||||
protected interface HystrixSetterFactoryClient {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
|
||||
HystrixCommand<List<Hello>> getHellosHystrix();
|
||||
|
||||
}
|
||||
|
||||
protected static class OtherArg {
|
||||
|
||||
public final String value;
|
||||
@@ -611,120 +413,40 @@ public class FeignClientTests {
|
||||
|
||||
}
|
||||
|
||||
static class HystrixClientFallbackFactory
|
||||
implements FallbackFactory<HystrixClientWithFallBackFactory> {
|
||||
|
||||
@Override
|
||||
public HystrixClientWithFallBackFactory create(final Throwable cause) {
|
||||
return new HystrixClientWithFallBackFactory() {
|
||||
@Override
|
||||
public Hello fail() {
|
||||
assertThat(cause).isNotNull().as("Cause was null");
|
||||
return new Hello(
|
||||
"Hello from the fallback side: " + cause.getMessage());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class InvalidTypeHystrixClientFallbackFactory
|
||||
implements FallbackFactory<String> {
|
||||
|
||||
@Override
|
||||
public String create(final Throwable cause) {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NullHystrixClientFallbackFactory implements FallbackFactory<String> {
|
||||
|
||||
@Override
|
||||
public String create(final Throwable cause) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class HystrixClientFallback implements HystrixClient {
|
||||
|
||||
@Override
|
||||
public Hello fail() {
|
||||
return new Hello("fallback");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Hello> failSingle() {
|
||||
return Single.just(new Hello("fallbacksingle"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HystrixCommand<Hello> failCommand() {
|
||||
return new FallbackCommand<>(new Hello("fallbackcommand"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<Hello> failObservable() {
|
||||
return Observable.just(new Hello("fallbackobservable"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Hello> failFuture() {
|
||||
return new FallbackCommand<>(new Hello("fallbackfuture")).queue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TestHystrixSetterFactoryClientConfig {
|
||||
|
||||
public static final String SETTER_PREFIX = "SETTER-";
|
||||
|
||||
@Bean
|
||||
public SetterFactory commandKeyIsRequestLineSetterFactory() {
|
||||
return new SetterFactory() {
|
||||
@Override
|
||||
public HystrixCommand.Setter create(Target<?> target, Method method) {
|
||||
String groupKey = SETTER_PREFIX + target.name();
|
||||
RequestMapping requestMapping = method
|
||||
.getAnnotation(RequestMapping.class);
|
||||
String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " "
|
||||
+ requestMapping.path()[0];
|
||||
return HystrixCommand.Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
|
||||
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class,
|
||||
DecodingTestClient.class, HystrixClient.class,
|
||||
HystrixClientWithFallBackFactory.class, HystrixSetterFactoryClient.class,
|
||||
InvalidTypeHystrixClientWithFallBackFactory.class,
|
||||
NullHystrixClientWithFallBackFactory.class },
|
||||
@EnableFeignClients(
|
||||
clients = { TestClientServiceId.class, TestClient.class,
|
||||
DecodingTestClient.class },
|
||||
defaultConfiguration = TestDefaultFeignConfig.class)
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "localapp",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp1",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp2",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp3",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp4",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp5",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp6",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp7",
|
||||
configuration = LocalRibbonClientConfiguration.class) })
|
||||
/*
|
||||
* @RibbonClients({
|
||||
*
|
||||
* @RibbonClient(name = "localapp", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp1", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp2", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp3", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp4", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp5", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp6", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp7", configuration =
|
||||
* LocalRibbonClientConfiguration.class) })
|
||||
*/
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
@@ -735,27 +457,6 @@ public class FeignClientTests {
|
||||
.run(args);
|
||||
}
|
||||
|
||||
// needs to be in parent context to test multiple HystrixClient beans
|
||||
@Bean
|
||||
public HystrixClientFallback hystrixClientFallback() {
|
||||
return new HystrixClientFallback();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HystrixClientFallbackFactory hystrixClientFallbackFactory() {
|
||||
return new HystrixClientFallbackFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InvalidTypeHystrixClientFallbackFactory invalidTypeHystrixClientFallbackFactory() {
|
||||
return new InvalidTypeHystrixClientFallbackFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NullHystrixClientFallbackFactory nullHystrixClientFallbackFactory() {
|
||||
return new NullHystrixClientFallbackFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
FeignFormatterRegistrar feignFormatterRegistrar() {
|
||||
return new FeignFormatterRegistrar() {
|
||||
@@ -941,10 +642,10 @@ public class FeignClientTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,10 @@ package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
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.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -56,12 +53,15 @@ public class FeignClientValidationTests {
|
||||
|
||||
@Test
|
||||
public void validLoadBalanced() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class,
|
||||
FeignRibbonClientAutoConfiguration.class,
|
||||
GoodServiceIdConfiguration.class);
|
||||
assertThat(context.getBean(GoodServiceIdConfiguration.Client.class)).isNotNull();
|
||||
context.close();
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* AnnotationConfigApplicationContext context = new
|
||||
* AnnotationConfigApplicationContext( LoadBalancerAutoConfiguration.class,
|
||||
* RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class,
|
||||
* GoodServiceIdConfiguration.class);
|
||||
* assertThat(context.getBean(GoodServiceIdConfiguration.Client.class)).isNotNull(
|
||||
* ); context.close();
|
||||
*/
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Client;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -29,14 +27,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -93,10 +86,13 @@ public class FeignHttpClientTests {
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate).isInstanceOf(feign.httpclient.ApacheHttpClient.class);
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
* LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
* Client delegate = client.getDelegate();
|
||||
* assertThat(delegate).isInstanceOf(feign.httpclient.ApacheHttpClient.class);
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -141,11 +137,15 @@ public class FeignHttpClientTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "localapp",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp1",
|
||||
configuration = LocalRibbonClientConfiguration.class) })
|
||||
/*
|
||||
* @RibbonClients({
|
||||
*
|
||||
* @RibbonClient(name = "localapp", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp1", configuration =
|
||||
* LocalRibbonClientConfiguration.class) })
|
||||
*/
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application implements UserService {
|
||||
|
||||
@@ -258,10 +258,10 @@ public class FeignHttpClientTests {
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Client;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -29,14 +27,9 @@ 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.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -94,10 +87,13 @@ public class FeignOkHttpTests {
|
||||
|
||||
@Test
|
||||
public void testFeignClientType() throws IllegalAccessException {
|
||||
assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
Client delegate = client.getDelegate();
|
||||
assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class);
|
||||
// FIXME: 3.0.0
|
||||
/*
|
||||
* assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class);
|
||||
* LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient;
|
||||
* Client delegate = client.getDelegate();
|
||||
* assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class);
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,11 +136,15 @@ public class FeignOkHttpTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
|
||||
@RibbonClients({
|
||||
@RibbonClient(name = "localapp",
|
||||
configuration = LocalRibbonClientConfiguration.class),
|
||||
@RibbonClient(name = "localapp1",
|
||||
configuration = LocalRibbonClientConfiguration.class) })
|
||||
/*
|
||||
* @RibbonClients({
|
||||
*
|
||||
* @RibbonClient(name = "localapp", configuration =
|
||||
* LocalRibbonClientConfiguration.class),
|
||||
*
|
||||
* @RibbonClient(name = "localapp1", configuration =
|
||||
* LocalRibbonClientConfiguration.class) })
|
||||
*/
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application implements UserService {
|
||||
|
||||
@@ -257,10 +257,10 @@ public class FeignOkHttpTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.valid;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import io.vavr.collection.HashSet;
|
||||
import io.vavr.collection.Set;
|
||||
import org.junit.Test;
|
||||
@@ -28,12 +26,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
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;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -79,7 +74,8 @@ public class IterableParameterTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(clients = TestClient.class)
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "localapp", configuration =
|
||||
// LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
@@ -96,10 +92,10 @@ public class IterableParameterTests {
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.valid.scanning;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -25,12 +23,9 @@ 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.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||
import org.springframework.cloud.openfeign.testclients.TestClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -68,7 +63,8 @@ public class FeignClientEnvVarTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients(basePackages = { "${basepackage}" })
|
||||
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClient(name = "localapp", configuration =
|
||||
// LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
@@ -86,10 +82,10 @@ public class FeignClientEnvVarTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.openfeign.valid.scanning;
|
||||
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import feign.Client;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -26,12 +24,9 @@ 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.cloud.netflix.ribbon.RibbonClients;
|
||||
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;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -100,7 +95,7 @@ public class FeignClientScanningTests {
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableFeignClients // NO clients attribute. That's what this class is testing!
|
||||
@RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
|
||||
// @RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class)
|
||||
@Import(NoSecurityConfiguration.class)
|
||||
protected static class Application {
|
||||
|
||||
@@ -118,10 +113,10 @@ public class FeignClientScanningTests {
|
||||
@Value("${local.server.port}")
|
||||
private int port = 0;
|
||||
|
||||
@Bean
|
||||
public ServerList<Server> ribbonServerList() {
|
||||
return new StaticServerList<>(new Server("localhost", this.port));
|
||||
}
|
||||
/*
|
||||
* @Bean public ServerList<Server> ribbonServerList() { return new
|
||||
* StaticServerList<>(new Server("localhost", this.port)); }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user