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:
Ryan Baxter
2017-01-09 14:10:19 -05:00
19 changed files with 537 additions and 128 deletions

View File

@@ -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`.

View File

@@ -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();
}

View File

@@ -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 {
}

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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);
}
};
}
}

View File

@@ -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);
}

View File

@@ -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));
}
});
}

View File

@@ -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");
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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]));

View File

@@ -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;
}
};
}
}
}

View File

@@ -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();

View File

@@ -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()));