diff --git a/pom.xml b/pom.xml
index ef239e18..89ac5e98 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,13 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-commons
+ test-jar
+ test
+ ${spring-cloud-commons.version}
+
org.springframework.cloud
spring-cloud-config-dependencies
diff --git a/spring-cloud-netflix-core/pom.xml b/spring-cloud-netflix-core/pom.xml
index 3343772a..4260ddb0 100644
--- a/spring-cloud-netflix-core/pom.xml
+++ b/spring-cloud-netflix-core/pom.xml
@@ -44,6 +44,16 @@
spring-boot-starter-web
true
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ true
+
+
+ org.springframework.retry
+ spring-retry
+ true
+
org.springframework.cloud
spring-cloud-commons
@@ -183,6 +193,12 @@
spring-boot-starter-test
test
+
+ org.springframework.cloud
+ spring-cloud-commons
+ test-jar
+ test
+
org.aspectj
aspectjweaver
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java
index a9729321..55f48ecb 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java
@@ -28,16 +28,20 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
+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.ConditionalOnProperty;
import org.springframework.cloud.client.actuator.HasFeatures;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
+import org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
+import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
import com.netflix.client.IClient;
@@ -78,6 +82,12 @@ public class RibbonAutoConfiguration {
return new RibbonLoadBalancerClient(springClientFactory());
}
+ @Bean
+ @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
+ public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
+ return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
+ }
+
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancedRetryPolicyFactory.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancedRetryPolicyFactory.java
new file mode 100644
index 00000000..563071c3
--- /dev/null
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancedRetryPolicyFactory.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.cloud.netflix.ribbon;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
+import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
+import org.springframework.http.HttpMethod;
+
+/**
+ * @author Ryan Baxter
+ */
+public class RibbonLoadBalancedRetryPolicyFactory implements LoadBalancedRetryPolicyFactory {
+
+ private SpringClientFactory clientFactory;
+
+ public RibbonLoadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
+ this.clientFactory = clientFactory;
+ }
+
+ @Override
+ public LoadBalancedRetryPolicy create(final String serviceId, final ServiceInstanceChooser loadBalanceChooser) {
+ final RibbonLoadBalancerContext lbContext = this.clientFactory
+ .getLoadBalancerContext(serviceId);
+ return new LoadBalancedRetryPolicy() {
+ private int sameServerCount = 0;
+ private int nextServerCount = 0;
+ private ServiceInstance lastServiceInstance = null;
+ public boolean canRetry(LoadBalancedRetryContext context) {
+ HttpMethod method = context.getRequest().getMethod();
+ return HttpMethod.GET == method || lbContext.isOkToRetryOnAllOperations();
+ }
+
+ @Override
+ public boolean canRetrySameServer(LoadBalancedRetryContext context) {
+ return sameServerCount < lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context);
+ }
+
+ @Override
+ public boolean canRetryNextServer(LoadBalancedRetryContext context) {
+ //this will be called after a failure occurs and we increment the counter
+ //so we check that the count is less than or equals to too make sure
+ //we try the next server the right number of times
+ return nextServerCount <= lbContext.getRetryHandler().getMaxRetriesOnNextServer() && canRetry(context);
+ }
+
+ @Override
+ public void close(LoadBalancedRetryContext context) {
+
+ }
+
+ @Override
+ public void registerThrowable(LoadBalancedRetryContext context, Throwable throwable) {
+ //Check if we need to ask the load balancer for a new server.
+ //Do this before we increment the counters because the first call to this method
+ //is not a retry it is just an initial failure.
+ if(!canRetrySameServer(context) && canRetryNextServer(context)) {
+ context.setServiceInstance(loadBalanceChooser.choose(serviceId));
+ }
+ //This method is called regardless of whether we are retrying or making the first request.
+ //Since we do not count the initial request in the retry count we don't reset the counter
+ //until we actually equal the same server count limit. This will allow us to make the initial
+ //request plus the right number of retries.
+ if(sameServerCount >= lbContext.getRetryHandler().getMaxRetriesOnSameServer() && canRetry(context)) {
+ //reset same server since we are moving to a new server
+ sameServerCount = 0;
+ nextServerCount++;
+ if(!canRetryNextServer(context)) {
+ context.setExhaustedOnly();
+ }
+ } else {
+ sameServerCount++;
+ }
+
+ }
+ };
+ }
+}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java
index c9261db5..414e8674 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java
@@ -20,15 +20,12 @@ import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
-
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
-import org.springframework.web.util.UriComponentsBuilder;
-
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
@@ -36,6 +33,7 @@ import com.netflix.loadbalancer.Server;
/**
* @author Spencer Gibb
* @author Dave Syer
+ * @author Ryan Baxter
*/
public class RibbonLoadBalancerClient implements LoadBalancerClient {
@@ -79,12 +77,25 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
+ return execute(serviceId, ribbonServer, request);
+ }
+
+ @Override
+ public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException {
+ Server server = null;
+ if(serviceInstance instanceof RibbonServer) {
+ server = ((RibbonServer)serviceInstance).getServer();
+ }
+ if (server == null) {
+ throw new IllegalStateException("No instances available for " + serviceId);
+ }
+
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
- T returnVal = request.apply(ribbonServer);
+ T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
}
diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java
index 41c3ebd9..aa9bb89e 100644
--- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java
+++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonInterceptorTests.java
@@ -16,9 +16,9 @@
package org.springframework.cloud.netflix.ribbon;
+import java.io.IOException;
import java.net.URI;
import java.net.URL;
-
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -35,7 +35,6 @@ import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.HttpRequestWrapper;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.util.UriComponentsBuilder;
-
import com.netflix.loadbalancer.Server;
import static org.junit.Assert.assertEquals;
@@ -105,6 +104,17 @@ public class RibbonInterceptorTests {
return null;
}
+ @Override
+ public T execute(String s, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException {
+ try {
+ return request.apply(this.instance);
+ }
+ catch (Exception ex) {
+ ReflectionUtils.rethrowRuntimeException(ex);
+ }
+ return null;
+ }
+
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
return UriComponentsBuilder.fromUri(original).host(instance.getHost())
diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancedRetryPolicyFactoryTest.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancedRetryPolicyFactoryTest.java
new file mode 100644
index 00000000..aecf19bc
--- /dev/null
+++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancedRetryPolicyFactoryTest.java
@@ -0,0 +1,214 @@
+package org.springframework.cloud.netflix.ribbon;
+
+import com.netflix.client.DefaultLoadBalancerRetryHandler;
+import com.netflix.client.config.CommonClientConfigKey;
+import com.netflix.client.config.IClientConfig;
+import com.netflix.loadbalancer.BaseLoadBalancer;
+import com.netflix.loadbalancer.LoadBalancerStats;
+import com.netflix.loadbalancer.Server;
+import com.netflix.loadbalancer.ServerStats;
+import org.junit.After;
+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.LoadBalancedRetryContext;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpRequest;
+import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.RibbonServer;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * @author Ryan Baxter
+ */
+public class RibbonLoadBalancedRetryPolicyFactoryTest {
+
+ @Mock
+ private SpringClientFactory clientFactory;
+
+ @Mock
+ private BaseLoadBalancer loadBalancer;
+
+ @Mock
+ private LoadBalancerStats loadBalancerStats;
+
+ @Mock
+ private ServerStats serverStats;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ given(this.clientFactory.getLoadBalancerContext(anyString())).willReturn(
+ new RibbonLoadBalancerContext(this.loadBalancer));
+ given(this.clientFactory.getInstance(anyString(), eq(ServerIntrospector.class)))
+ .willReturn(new DefaultServerIntrospector() {
+ @Override
+ public Map getMetadata(Server server) {
+ return Collections.singletonMap("mykey", "myvalue");
+ }
+ });
+
+ }
+
+ @After
+ public void tearDown() throws Exception {}
+
+ @Test
+ public void testGetRetryPolicyNoRetry() throws Exception {
+ int sameServer = 0;
+ int nextServer = 0;
+ boolean retryOnAllOps = false;
+ RibbonServer server = getRibbonServer();
+ IClientConfig config = mock(IClientConfig.class);
+ doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(sameServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
+ doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
+ doReturn(server.getServiceId()).when(config).getClientName();
+ doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
+ clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
+ RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
+ RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
+ LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
+ HttpRequest request = mock(HttpRequest.class);
+ doReturn(HttpMethod.GET).when(request).getMethod();
+ LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
+ assertThat(policy.canRetryNextServer(context), is(true));
+ assertThat(policy.canRetrySameServer(context), is(false));
+ }
+
+ @Test
+ public void testGetRetryPolicyNotGet() throws Exception {
+ int sameServer = 3;
+ int nextServer = 3;
+ boolean retryOnAllOps = false;
+ RibbonServer server = getRibbonServer();
+ IClientConfig config = mock(IClientConfig.class);
+ doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(sameServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
+ doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
+ doReturn(server.getServiceId()).when(config).getClientName();
+ doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
+ clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
+ RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
+ RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
+ LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
+ HttpRequest request = mock(HttpRequest.class);
+ doReturn(HttpMethod.POST).when(request).getMethod();
+ LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
+ assertThat(policy.canRetryNextServer(context), is(false));
+ assertThat(policy.canRetrySameServer(context), is(false));
+ }
+
+ @Test
+ public void testGetRetryPolicyRetryOnNonGet() throws Exception {
+ int sameServer = 3;
+ int nextServer = 3;
+ boolean retryOnAllOps = true;
+ RibbonServer server = getRibbonServer();
+ IClientConfig config = mock(IClientConfig.class);
+ doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(sameServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(nextServer).when(config).getPropertyAsInteger(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(retryOnAllOps).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
+ doReturn(retryOnAllOps).when(config).getPropertyAsBoolean(eq(CommonClientConfigKey.OkToRetryOnAllOperations), anyBoolean());
+ doReturn(server.getServiceId()).when(config).getClientName();
+ doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
+ clientFactory.getLoadBalancerContext(server.getServiceId()).initWithNiwsConfig(config);
+ RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
+ RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
+ LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
+ HttpRequest request = mock(HttpRequest.class);
+ doReturn(HttpMethod.POST).when(request).getMethod();
+ LoadBalancedRetryContext context = new LoadBalancedRetryContext(null, request);
+ assertThat(policy.canRetryNextServer(context), is(true));
+ assertThat(policy.canRetrySameServer(context), is(true));
+ }
+
+ @Test
+ public void testGetRetryPolicyRetryCount() throws Exception {
+ int sameServer = 3;
+ int nextServer = 3;
+ RibbonServer server = getRibbonServer();
+ IClientConfig config = mock(IClientConfig.class);
+ doReturn(sameServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt());
+ doReturn(nextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt());
+ doReturn(false).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false));
+ doReturn(config).when(clientFactory).getClientConfig(eq(server.getServiceId()));
+ clientFactory.getLoadBalancerContext(server.getServiceId()).setRetryHandler(new DefaultLoadBalancerRetryHandler(config));
+ RibbonLoadBalancerClient client = getRibbonLoadBalancerClient(server);
+ RibbonLoadBalancedRetryPolicyFactory factory = new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
+ LoadBalancedRetryPolicy policy = factory.create(server.getServiceId(), client);
+ HttpRequest request = mock(HttpRequest.class);
+ doReturn(HttpMethod.GET).when(request).getMethod();
+ LoadBalancedRetryContext context = spy(new LoadBalancedRetryContext(null, request));
+ //Loop through as if we are retrying a request until we exhaust the number of retries
+ //outer loop is for next server retries
+ //inner loop is for same server retries
+ for(int i = 0; i < nextServer + 1; i++) {
+ //iterate once time beyond the same server retry limit to cause us to reset
+ //the same sever counter and increment the next server counter
+ for(int j = 0; j < sameServer + 1; j++) {
+ if(j < 3) {
+ assertThat(policy.canRetrySameServer(context), is(true));
+ } else {
+ assertThat(policy.canRetrySameServer(context), is(false));
+ }
+ policy.registerThrowable(context, new IOException());
+ }
+ if(i < 3) {
+ assertThat(policy.canRetryNextServer(context), is(true));
+ } else {
+ assertThat(policy.canRetryNextServer(context), is(false));
+ }
+ }
+ assertThat(context.isExhaustedOnly(), is(true));
+ verify(context, times(4)).setServiceInstance(any(ServiceInstance.class));
+ }
+
+ protected RibbonLoadBalancerClient getRibbonLoadBalancerClient(
+ RibbonServer ribbonServer) {
+ given(this.loadBalancer.getName()).willReturn(ribbonServer.getServiceId());
+ given(this.loadBalancer.chooseServer(anyObject())).willReturn(
+ ribbonServer.getServer());
+ given(this.loadBalancer.getLoadBalancerStats())
+ .willReturn(this.loadBalancerStats);
+ given(this.loadBalancerStats.getSingleServerStat(ribbonServer.getServer()))
+ .willReturn(this.serverStats);
+ given(this.clientFactory.getLoadBalancer(this.loadBalancer.getName()))
+ .willReturn(this.loadBalancer);
+ return new RibbonLoadBalancerClient(this.clientFactory);
+ }
+
+ protected RibbonServer getRibbonServer() {
+ return new RibbonServer("testService", new Server("myhost", 9080), false,
+ Collections.singletonMap("mykey", "myvalue"));
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringRetryDisabledTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringRetryDisabledTests.java
new file mode 100644
index 00000000..3317c567
--- /dev/null
+++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringRetryDisabledTests.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * * Copyright 2013-2016 the original author or authors.
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.springframework.cloud.netflix.ribbon;
+
+import java.util.Map;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cloud.ClassPathExclusions;
+import org.springframework.cloud.FilteredClassPathRunner;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+
+/**
+ * @author Ryan Baxter
+ */
+@RunWith(FilteredClassPathRunner.class)
+@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"})
+public class SpringRetryDisabledTests {
+
+ private ConfigurableApplicationContext context;
+
+ @Before
+ public void setUp() {
+ context = new SpringApplicationBuilder().web(false)
+ .sources(RibbonAutoConfiguration.class,LoadBalancerAutoConfiguration.class).run();
+ }
+
+ @After
+ public void tearDown() {
+ if(context != null) {
+ context.close();
+ }
+ }
+
+ @Test
+ public void testLoadBalancedRetryFactoryBean() throws Exception {
+ Map factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
+ assertThat(factories.values(), hasSize(0));
+ }
+}
diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringRetryEnabledTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringRetryEnabledTests.java
new file mode 100644
index 00000000..539b94bc
--- /dev/null
+++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/ribbon/SpringRetryEnabledTests.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * * Copyright 2013-2016 the original author or authors.
+ * *
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * * you may not use this file except in compliance with the License.
+ * * You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.springframework.cloud.netflix.ribbon;
+
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.BeansException;
+import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicyFactory;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
+
+/**
+ * @author Ryan Baxter
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = {RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class})
+public class SpringRetryEnabledTests implements ApplicationContextAware {
+
+ private ApplicationContext context;
+
+ @Test
+ public void testLoadBalancedRetryFactoryBean() throws Exception {
+ Map factories = context.getBeansOfType(LoadBalancedRetryPolicyFactory.class);
+ assertThat(factories.values(), hasSize(1));
+ assertThat(factories.values().toArray()[0], instanceOf(RibbonLoadBalancedRetryPolicyFactory.class));
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext context) throws BeansException {
+ this.context = context;
+ }
+}
diff --git a/spring-cloud-starter-ribbon/pom.xml b/spring-cloud-starter-ribbon/pom.xml
index 4a31be7c..549ad625 100644
--- a/spring-cloud-starter-ribbon/pom.xml
+++ b/spring-cloud-starter-ribbon/pom.xml
@@ -20,6 +20,14 @@
${basedir}/../..
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.retry
+ spring-retry
+
org.springframework.cloud
spring-cloud-starter