Merge remote-tracking branch 'Upstream/master' into error-handle-invalid-hostnames-159
# Conflicts: # spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerInterceptor.java
This commit is contained in:
@@ -217,10 +217,10 @@ application that includes that jar on its classpath.
|
||||
|
||||
=== Environment Changes
|
||||
|
||||
The application will listen for an `EnvironmentChangedEvent` and react
|
||||
The application will listen for an `EnvironmentChangeEvent` and react
|
||||
to the change in a couple of standard ways (additional
|
||||
`ApplicationListeners` can be added as `@Beans` by the user in the
|
||||
normal way). When an `EnvironmentChangedEvent` is observed it will
|
||||
normal way). When an `EnvironmentChangeEvent` is observed it will
|
||||
have a list of key values that have changed, and the application will
|
||||
use those to:
|
||||
|
||||
@@ -231,12 +231,12 @@ Note that the Config Client does not by default poll for changes in
|
||||
the `Environment`, and generally we would not recommend that approach
|
||||
for detecting changes (although you could set it up with a
|
||||
`@Scheduled` annotation). If you have a scaled-out client application
|
||||
then it is better to broadcast the `EnvironmentChangedEvent` to all
|
||||
then it is better to broadcast the `EnvironmentChangeEvent` to all
|
||||
the instances instead of having them polling for changes (e.g. using
|
||||
the https://github.com/spring-cloud/spring-cloud-bus[Spring Cloud
|
||||
Bus]).
|
||||
|
||||
The `EnvironmentChangedEvent` covers a large class of refresh use
|
||||
The `EnvironmentChangeEvent` covers a large class of refresh use
|
||||
cases, as long as you can actually make a change to the `Environment`
|
||||
and publish the event (those APIs are public and part of core
|
||||
Spring). You can verify the changes are bound to
|
||||
@@ -363,7 +363,7 @@ for details of how the `RestTemplate` is set up.
|
||||
|
||||
A load balanced `RestTemplate` can be configured to retry failed requests.
|
||||
By default this logic is disabled, you can enable it by setting
|
||||
`spring.cloud.loadbalancer.retry=true`. The load balanced `RestTemplate` will
|
||||
`spring.cloud.loadbalancer.retry.enabled=true`. The load balanced `RestTemplate` will
|
||||
honor some of the Ribbon configuration values related to retrying failed requests.
|
||||
The properties you can use are `client.ribbon.MaxAutoRetries`,
|
||||
`client.ribbon.MaxAutoRetriesNextServer`, and `client.ribbon.OkToRetryOnAllOperations`.
|
||||
|
||||
@@ -31,23 +31,25 @@ public interface DiscoveryClient {
|
||||
* A human readable description of the implementation, used in HealthIndicator
|
||||
* @return the description
|
||||
*/
|
||||
public String description();
|
||||
String description();
|
||||
|
||||
/**
|
||||
* @deprecated use the {@link org.springframework.cloud.client.serviceregistry.Registration} bean instead
|
||||
*
|
||||
* @return ServiceInstance with information used to register the local service
|
||||
*/
|
||||
public ServiceInstance getLocalServiceInstance();
|
||||
ServiceInstance getLocalServiceInstance();
|
||||
|
||||
/**
|
||||
* Get all ServiceInstances associated with a particular serviceId
|
||||
* @param serviceId the serviceId to query
|
||||
* @return a List of ServiceInstance
|
||||
*/
|
||||
public List<ServiceInstance> getInstances(String serviceId);
|
||||
List<ServiceInstance> getInstances(String serviceId);
|
||||
|
||||
/**
|
||||
* @return all known service ids
|
||||
*/
|
||||
public List<String> getServices();
|
||||
List<String> getServices();
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @deprecated use {@link org.springframework.cloud.client.serviceregistry.AutoServiceRegistration} instead. This class will be removed in the next release train.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface DiscoveryLifecycle extends SmartLifecycle, Ordered {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -39,6 +38,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Will Tran
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(RestTemplate.class)
|
||||
@@ -65,12 +65,24 @@ public class LoadBalancerAutoConfiguration {
|
||||
};
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public LoadBalancerRequestFactory loadBalancerRequestFactory(
|
||||
LoadBalancerClient loadBalancerClient) {
|
||||
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
|
||||
static class LoadBalancerInterceptorConfig {
|
||||
@Bean
|
||||
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient) {
|
||||
return new LoadBalancerInterceptor(loadBalancerClient);
|
||||
public LoadBalancerInterceptor ribbonInterceptor(
|
||||
LoadBalancerClient loadBalancerClient,
|
||||
LoadBalancerRequestFactory requestFactory) {
|
||||
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -108,8 +120,10 @@ public class LoadBalancerAutoConfiguration {
|
||||
@Bean
|
||||
public RetryLoadBalancerInterceptor ribbonInterceptor(
|
||||
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory) {
|
||||
return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate(), properties, lbRetryPolicyFactory);
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
|
||||
LoadBalancerRequestFactory requestFactory) {
|
||||
return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate(), properties,
|
||||
lbRetryPolicyFactory, requestFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,7 @@ package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
@@ -29,13 +29,21 @@ import org.springframework.util.Assert;
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
* @author William Tran
|
||||
*/
|
||||
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private LoadBalancerClient loadBalancer;
|
||||
private LoadBalancerRequestFactory requestFactory;
|
||||
|
||||
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
this.requestFactory = requestFactory;
|
||||
}
|
||||
|
||||
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
// for backwards compatibility
|
||||
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,16 +52,6 @@ public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
|
||||
final URI originalUri = request.getURI();
|
||||
String serviceName = originalUri.getHost();
|
||||
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
|
||||
return this.loadBalancer.execute(serviceName,
|
||||
new LoadBalancerRequest<ClientHttpResponse>() {
|
||||
@Override
|
||||
public ClientHttpResponse apply(final ServiceInstance instance)
|
||||
throws Exception {
|
||||
HttpRequest serviceRequest = new ServiceRequestWrapper(request,
|
||||
instance, loadBalancer);
|
||||
return execution.execute(serviceRequest, body);
|
||||
}
|
||||
|
||||
});
|
||||
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
/**
|
||||
* Creates {@link LoadBalancerRequest}s for {@link LoadBalancerInterceptor} and
|
||||
* {@link RetryLoadBalancerInterceptor}. Applies
|
||||
* {@link LoadBalancerRequestTransformer}s to the intercepted
|
||||
* {@link HttpRequest}.
|
||||
*
|
||||
* @author William Tran
|
||||
*
|
||||
*/
|
||||
public class LoadBalancerRequestFactory {
|
||||
|
||||
private LoadBalancerClient loadBalancer;
|
||||
private List<LoadBalancerRequestTransformer> transformers;
|
||||
|
||||
public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer,
|
||||
List<LoadBalancerRequestTransformer> transformers) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
this.transformers = transformers;
|
||||
}
|
||||
|
||||
public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
}
|
||||
|
||||
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
|
||||
final byte[] body, final ClientHttpRequestExecution execution) {
|
||||
return new LoadBalancerRequest<ClientHttpResponse>() {
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse apply(final ServiceInstance instance)
|
||||
throws Exception {
|
||||
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
|
||||
if (transformers != null) {
|
||||
for (LoadBalancerRequestTransformer transformer : transformers) {
|
||||
serviceRequest = transformer.transformRequest(serviceRequest, instance);
|
||||
}
|
||||
}
|
||||
return execution.execute(serviceRequest, body);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* Allows applications to transform the load balanced {@link HttpRequest} given
|
||||
* the chosen {@link ServiceInstance}
|
||||
*
|
||||
* @author Will Tran
|
||||
*/
|
||||
@Order(LoadBalancerRequestTransformer.DEFAULT_ORDER)
|
||||
public interface LoadBalancerRequestTransformer {
|
||||
public static final int DEFAULT_ORDER = 0;
|
||||
|
||||
HttpRequest transformRequest(HttpRequest request, ServiceInstance instance);
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -16,6 +32,7 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
* @author Will Tran
|
||||
*/
|
||||
public class RetryLoadBalancerInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
@@ -23,15 +40,26 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
|
||||
private RetryTemplate retryTemplate;
|
||||
private LoadBalancerClient loadBalancer;
|
||||
private LoadBalancerRetryProperties lbProperties;
|
||||
private LoadBalancerRequestFactory requestFactory;
|
||||
|
||||
|
||||
public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer, RetryTemplate retryTemplate,
|
||||
LoadBalancerRetryProperties lbProperties,
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory) {
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
|
||||
LoadBalancerRequestFactory requestFactory) {
|
||||
this.loadBalancer = loadBalancer;
|
||||
this.lbRetryPolicyFactory = lbRetryPolicyFactory;
|
||||
this.retryTemplate = retryTemplate;
|
||||
this.lbProperties = lbProperties;
|
||||
this.requestFactory = requestFactory;
|
||||
}
|
||||
|
||||
public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer, RetryTemplate retryTemplate,
|
||||
LoadBalancerRetryProperties lbProperties,
|
||||
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory) {
|
||||
// for backwards compatibility
|
||||
this(loadBalancer, retryTemplate, lbProperties, lbRetryPolicyFactory,
|
||||
new LoadBalancerRequestFactory(loadBalancer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,18 +89,7 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto
|
||||
}
|
||||
return RetryLoadBalancerInterceptor.this.loadBalancer.execute(
|
||||
serviceName, serviceInstance,
|
||||
new LoadBalancerRequest<ClientHttpResponse>() {
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse apply(
|
||||
final ServiceInstance instance)
|
||||
throws Exception {
|
||||
HttpRequest serviceRequest = new ServiceRequestWrapper(
|
||||
request, instance, loadBalancer);
|
||||
return execution.execute(serviceRequest, body);
|
||||
}
|
||||
|
||||
});
|
||||
requestFactory.createRequest(request, body, execution));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springframework.cloud.client.serviceregistry;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
|
||||
public class AutoServiceRegistrationAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private AutoServiceRegistration autoServiceRegistration;
|
||||
|
||||
@Autowired
|
||||
private AutoServiceRegistrationProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
protected void init() {
|
||||
if (autoServiceRegistration == null && this.properties.isFailFast()) {
|
||||
throw new IllegalStateException("Auto Service Registration has been requested, but there is no AutoServiceRegistration bean");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,12 @@
|
||||
package org.springframework.cloud.client.serviceregistry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
|
||||
public class AutoServiceRegistrationConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private AutoServiceRegistration autoServiceRegistration;
|
||||
|
||||
@Autowired
|
||||
private AutoServiceRegistrationProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
protected void init() {
|
||||
if (autoServiceRegistration == null && this.properties.isFailFast()) {
|
||||
throw new IllegalStateException("Auto Service Registration has been requested, but there is no AutoServiceRegistration bean");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ConfigurationProperties("spring.cloud.service-registry.auto-registration")
|
||||
public class AutoServiceRegistrationProperties {
|
||||
|
||||
/** If Auto-Service Registration is enabled, default to true. */
|
||||
private boolean enabled = true;
|
||||
|
||||
/** Should startup fail if there is no AutoServiceRegistration, default to false. */
|
||||
private boolean failFast = false;
|
||||
|
||||
|
||||
@@ -11,18 +11,20 @@ import org.springframework.context.annotation.Configuration;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@ConditionalOnBean(ServiceRegistry.class)
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
@Configuration
|
||||
public class ServiceRegistryAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private Registration registration;
|
||||
@ConditionalOnBean(ServiceRegistry.class)
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
protected class ServiceRegistryEndpointConfiguration {
|
||||
@Autowired(required = false)
|
||||
private Registration registration;
|
||||
|
||||
@Bean
|
||||
public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) {
|
||||
ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry);
|
||||
endpoint.setRegistration(registration);
|
||||
return endpoint;
|
||||
@Bean
|
||||
public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) {
|
||||
ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry);
|
||||
endpoint.setRegistration(registration);
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LoadBalancerRequestFactoryConfigurationTests {
|
||||
|
||||
@Mock
|
||||
private HttpRequest request;
|
||||
@Mock
|
||||
private HttpRequest transformedRequest;
|
||||
@Mock
|
||||
private HttpRequest transformedRequest2;
|
||||
@Mock
|
||||
private ClientHttpRequestExecution execution;
|
||||
@Mock
|
||||
private ServiceInstance instance;
|
||||
|
||||
private byte[] body = new byte[] {};
|
||||
private ArgumentCaptor<HttpRequest> httpRequestCaptor;
|
||||
private LoadBalancerRequestFactory lbReqFactory;
|
||||
private LoadBalancerRequest<?> lbRequest;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
httpRequestCaptor = ArgumentCaptor.forClass(HttpRequest.class);
|
||||
}
|
||||
|
||||
protected ConfigurableApplicationContext init(Class<?> config) {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder().web(false)
|
||||
.properties("spring.aop.proxyTargetClass=true")
|
||||
.sources(config, LoadBalancerAutoConfiguration.class).run();
|
||||
|
||||
lbReqFactory = context.getBean(LoadBalancerRequestFactory.class);
|
||||
lbRequest = lbReqFactory.createRequest(request, body, execution);
|
||||
return context;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformer() throws Exception {
|
||||
ConfigurableApplicationContext context = init(Transformer.class);
|
||||
|
||||
LoadBalancerRequestTransformer transformer = context.getBean("transformer",
|
||||
LoadBalancerRequestTransformer.class);
|
||||
when(transformer.transformRequest(any(ServiceRequestWrapper.class), eq(instance)))
|
||||
.thenReturn(transformedRequest);
|
||||
|
||||
lbRequest.apply(instance);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
assertEquals(
|
||||
"transformer should have transformed the ServiceRequestWrapper into transformedRequest",
|
||||
transformedRequest,
|
||||
httpRequestCaptor.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTransformer() throws Exception {
|
||||
init(NoTransformer.class);
|
||||
|
||||
lbRequest.apply(instance);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
assertEquals(
|
||||
"ServiceRequestWrapper should be executed",
|
||||
ServiceRequestWrapper.class,
|
||||
httpRequestCaptor.getValue().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformersAreOrdered() throws Exception {
|
||||
ConfigurableApplicationContext context = init(TransformersAreOrdered.class);
|
||||
|
||||
LoadBalancerRequestTransformer transformer = context.getBean("transformer",
|
||||
LoadBalancerRequestTransformer.class);
|
||||
when(transformer.transformRequest(any(ServiceRequestWrapper.class), eq(instance)))
|
||||
.thenReturn(transformedRequest);
|
||||
LoadBalancerRequestTransformer transformer2 = context.getBean("transformer2",
|
||||
LoadBalancerRequestTransformer.class);
|
||||
when(transformer2.transformRequest(transformedRequest, instance)).thenReturn(transformedRequest2);
|
||||
|
||||
lbRequest.apply(instance);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
assertEquals(
|
||||
"transformer2 should run after transformer",
|
||||
transformedRequest2,
|
||||
httpRequestCaptor.getValue());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Transformer {
|
||||
|
||||
@Bean
|
||||
public LoadBalancerClient loadBalancerClient() {
|
||||
return mock(LoadBalancerClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LoadBalancerRequestTransformer transformer() {
|
||||
return mock(LoadBalancerRequestTransformer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TransformersAreOrdered {
|
||||
|
||||
@Bean
|
||||
public LoadBalancerClient loadBalancerClient() {
|
||||
return mock(LoadBalancerClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LoadBalancerRequestTransformer transformer() {
|
||||
return mock(LoadBalancerRequestTransformer.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(LoadBalancerRequestTransformer.DEFAULT_ORDER + 1)
|
||||
public LoadBalancerRequestTransformer transformer2() {
|
||||
return mock(LoadBalancerRequestTransformer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class NoTransformer {
|
||||
|
||||
@Bean
|
||||
public LoadBalancerClient loadBalancerClient() {
|
||||
return mock(LoadBalancerClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.client.loadbalancer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LoadBalancerRequestFactoryTests {
|
||||
|
||||
@Mock
|
||||
private LoadBalancerClient loadBalancer;
|
||||
@Mock
|
||||
private HttpRequest request;
|
||||
@Mock
|
||||
private HttpRequest transformedRequest1;
|
||||
@Mock
|
||||
private HttpRequest transformedRequest2;
|
||||
|
||||
private byte[] body = new byte[] {};
|
||||
|
||||
@Mock
|
||||
private ClientHttpRequestExecution execution;
|
||||
@Mock
|
||||
private ServiceInstance instance;
|
||||
@Mock
|
||||
private LoadBalancerRequestTransformer transformer1;
|
||||
@Mock
|
||||
private LoadBalancerRequestTransformer transformer2;
|
||||
|
||||
private ArgumentCaptor<HttpRequest> httpRequestCaptor;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
httpRequestCaptor = ArgumentCaptor.forClass(HttpRequest.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullTransformers() throws Exception {
|
||||
executeLbRequest(null);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
Assert.assertEquals("request should be of type ServiceRequestWrapper", ServiceRequestWrapper.class,
|
||||
httpRequestCaptor.getValue().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyTransformers() throws Exception {
|
||||
List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
|
||||
|
||||
executeLbRequest(transformers);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
Assert.assertEquals("request should be of type ServiceRequestWrapper", ServiceRequestWrapper.class,
|
||||
httpRequestCaptor.getValue().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneTransformer() throws Exception {
|
||||
List<LoadBalancerRequestTransformer> transformers = Arrays.asList(transformer1);
|
||||
when(transformer1.transformRequest(any(ServiceRequestWrapper.class), eq(instance))).thenReturn(transformedRequest1);
|
||||
|
||||
executeLbRequest(transformers);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
assertEquals("transformer1 should have transformed request into transformedRequest1", transformedRequest1,
|
||||
httpRequestCaptor.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoTransformers() throws Exception {
|
||||
List<LoadBalancerRequestTransformer> transformers = Arrays.asList(transformer1, transformer2);
|
||||
when(transformer1.transformRequest(any(ServiceRequestWrapper.class), eq(instance))).thenReturn(transformedRequest1);
|
||||
when(transformer2.transformRequest(transformedRequest1, instance))
|
||||
.thenReturn(transformedRequest2);
|
||||
|
||||
executeLbRequest(transformers);
|
||||
|
||||
verify(execution).execute(httpRequestCaptor.capture(), eq(body));
|
||||
assertEquals("transformer2 should have transformed transformedRequest1 into transformedRequest2",
|
||||
transformedRequest2,
|
||||
httpRequestCaptor.getValue());
|
||||
}
|
||||
|
||||
private void executeLbRequest(List<LoadBalancerRequestTransformer> transformers) throws Exception {
|
||||
LoadBalancerRequestFactory lbReqFactory = new LoadBalancerRequestFactory(loadBalancer, transformers);
|
||||
LoadBalancerRequest<ClientHttpResponse> lbRequest = lbReqFactory.createRequest(request, body, execution);
|
||||
lbRequest.apply(instance);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,12 +36,14 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
private LoadBalancerClient client;
|
||||
private RetryTemplate retryTemplate;
|
||||
private LoadBalancerRetryProperties lbProperties;
|
||||
private LoadBalancerRequestFactory lbRequestFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
client = mock(LoadBalancerClient.class);
|
||||
retryTemplate = spy(new RetryTemplate());
|
||||
lbProperties = new LoadBalancerRetryProperties();
|
||||
lbRequestFactory = mock(LoadBalancerRequestFactory.class);
|
||||
|
||||
}
|
||||
|
||||
@@ -63,11 +65,12 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
|
||||
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenThrow(new IOException());
|
||||
lbProperties.setEnabled(false);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
|
||||
byte[] body = new byte[]{};
|
||||
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
|
||||
interceptor.intercept(request, body, execution);
|
||||
verify(retryTemplate, times(1)).setRetryPolicy(any(NeverRetryPolicy.class));
|
||||
verify(lbRequestFactory).createRequest(request, body, execution);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@@ -100,11 +103,12 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
|
||||
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenReturn(clientHttpResponse);
|
||||
lbProperties.setEnabled(true);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
|
||||
byte[] body = new byte[]{};
|
||||
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
|
||||
interceptor.intercept(request, body, execution);
|
||||
verify(retryTemplate, times(1)).setRetryPolicy(any(NeverRetryPolicy.class));
|
||||
verify(lbRequestFactory).createRequest(request, body, execution);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,12 +124,13 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
|
||||
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenReturn(clientHttpResponse);
|
||||
lbProperties.setEnabled(true);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
|
||||
byte[] body = new byte[]{};
|
||||
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
|
||||
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
|
||||
assertThat(rsp, is(clientHttpResponse));
|
||||
verify(retryTemplate, times(1)).setRetryPolicy(eq(interceptorRetryPolicy));
|
||||
verify(lbRequestFactory).createRequest(request, body, execution);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -141,13 +146,14 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
|
||||
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenThrow(new IOException()).thenReturn(clientHttpResponse);
|
||||
lbProperties.setEnabled(true);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
|
||||
byte[] body = new byte[]{};
|
||||
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
|
||||
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
|
||||
verify(client, times(2)).execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class));
|
||||
assertThat(rsp, is(clientHttpResponse));
|
||||
verify(retryTemplate, times(1)).setRetryPolicy(any(InterceptorRetryPolicy.class));
|
||||
verify(lbRequestFactory, times(2)).createRequest(request, body, execution);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
@@ -164,9 +170,10 @@ public class RetryLoadBalancerInterceptorTest {
|
||||
when(client.choose(eq("foo"))).thenReturn(serviceInstance);
|
||||
when(client.execute(eq("foo"), eq(serviceInstance), any(LoadBalancerRequest.class))).thenThrow(new IOException()).thenReturn(clientHttpResponse);
|
||||
lbProperties.setEnabled(true);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory);
|
||||
RetryLoadBalancerInterceptor interceptor = new RetryLoadBalancerInterceptor(client, retryTemplate, lbProperties, lbRetryPolicyFactory, lbRequestFactory);
|
||||
byte[] body = new byte[]{};
|
||||
ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class);
|
||||
ClientHttpResponse rsp = interceptor.intercept(request, body, execution);
|
||||
verify(lbRequestFactory).createRequest(request, body, execution);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AutoServiceRegistrationConfigurationTests {
|
||||
public class AutoServiceRegistrationAutoConfigurationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
@@ -69,6 +69,7 @@ public class AutoServiceRegistrationConfigurationTests {
|
||||
private AnnotationConfigApplicationContext setup(String property, Class... classes) {
|
||||
ArrayList<Class> list = new ArrayList<>();
|
||||
list.add(AutoServiceRegistrationConfiguration.class);
|
||||
list.add(AutoServiceRegistrationAutoConfiguration.class);
|
||||
list.addAll(Arrays.asList(classes));
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(list.toArray(new Class[0]));
|
||||
@@ -58,13 +58,6 @@ import org.springframework.integration.monitor.IntegrationMBeanExporter;
|
||||
@AutoConfigureAfter(EndpointAutoConfiguration.class)
|
||||
public class RefreshEndpointAutoConfiguration {
|
||||
|
||||
@ConditionalOnBean(EndpointAutoConfiguration.class)
|
||||
@ConditionalOnMissingClass("org.springframework.boot.actuate.info.InfoContributor")
|
||||
@Bean
|
||||
InfoEndpointRebinderConfiguration infoEndpointRebinderConfiguration() {
|
||||
return new InfoEndpointRebinderConfiguration();
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledHealthIndicator("refresh")
|
||||
@Bean
|
||||
@@ -133,52 +126,4 @@ public class RefreshEndpointAutoConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class InfoEndpointRebinderConfiguration
|
||||
implements ApplicationListener<EnvironmentChangeEvent>, BeanPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EnvironmentChangeEvent event) {
|
||||
for (String key : event.getKeys()) {
|
||||
if (key.startsWith("info.")) {
|
||||
this.map.put(key.substring("info.".length()),
|
||||
this.environment.getProperty(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof InfoEndpoint) {
|
||||
return infoEndpoint((InfoEndpoint) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private InfoEndpoint infoEndpoint(InfoEndpoint endpoint) {
|
||||
return new InfoEndpoint(endpoint.invoke()) {
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
Map<String, Object> info = new LinkedHashMap<String, Object>(
|
||||
super.invoke());
|
||||
info.putAll(InfoEndpointRebinderConfiguration.this.map);
|
||||
return info;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package org.springframework.cloud.context.named;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -61,6 +63,10 @@ public abstract class NamedContextFactory<C extends NamedContextFactory.Specific
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getContextNames() {
|
||||
return new HashSet<>(contexts.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
Collection<AnnotationConfigApplicationContext> values = this.contexts.values();
|
||||
|
||||
@@ -12,6 +12,7 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -37,6 +38,8 @@ public class NamedContextFactoryTests {
|
||||
Bar bar = factory.getInstance("bar", Bar.class);
|
||||
assertThat("bar was null", bar, is(notNullValue()));
|
||||
|
||||
assertThat("context names not exposed", factory.getContextNames(), hasItems("foo", "bar"));
|
||||
|
||||
Bar foobar = factory.getInstance("foo", Bar.class);
|
||||
assertThat("bar was not null", foobar, is(nullValue()));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user