From d78da7b99451425f32dd0446877a65354871c489 Mon Sep 17 00:00:00 2001 From: Erik Kringen Date: Fri, 10 Feb 2017 12:37:17 -0600 Subject: [PATCH] Add support for Feign.SetterFactory (#1673) Fixes gh-1414 --- .../main/asciidoc/spring-cloud-netflix.adoc | 1 + .../cloud/netflix/feign/HystrixTargeter.java | 15 +++- .../netflix/feign/valid/FeignClientTests.java | 82 ++++++++++++++++--- 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 110fb6a9..00b26bf5 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -976,6 +976,7 @@ Spring Cloud Netflix _does not_ provide the following beans by default for feign * `ErrorDecoder` * `Request.Options` * `Collection` +* `SetterFactory` Creating a bean of one of those type and placing it in a `@FeignClient` configuration (such as `FooConfiguration` above) allows you to override each one of the beans described. Example: diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java index 66106c70..cfbe3659 100644 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java +++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/feign/HystrixTargeter.java @@ -17,14 +17,17 @@ package org.springframework.cloud.netflix.feign; +import org.springframework.util.Assert; + import feign.Feign; import feign.Target; import feign.hystrix.FallbackFactory; import feign.hystrix.HystrixFeign; -import org.springframework.util.Assert; +import feign.hystrix.SetterFactory; /** * @author Spencer Gibb + * @author Erik Kringen */ @SuppressWarnings("unchecked") class HystrixTargeter implements Targeter { @@ -36,6 +39,11 @@ class HystrixTargeter implements Targeter { return feign.target(target); } feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; + SetterFactory setterFactory = getOptional(factory.getName(), context, + SetterFactory.class); + if (setterFactory != null) { + builder.setterFactory(setterFactory); + } Class fallback = factory.getFallback(); if (fallback != void.class) { return targetWithFallback(factory.getName(), context, target, builder, fallback); @@ -95,4 +103,9 @@ class HystrixTargeter implements Targeter { } return (T) fallbackInstance; } + + private T getOptional(String feignClientName, FeignContext context, + Class beanType) { + return context.getInstance(feignClientName, beanType); + } } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java index 903fad03..d928b28c 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignClientTests.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.text.ParseException; import java.util.ArrayList; @@ -68,14 +69,19 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.netflix.hystrix.HystrixCommand; +import com.netflix.hystrix.HystrixCommandGroupKey; +import com.netflix.hystrix.HystrixCommandKey; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; import feign.Client; +import feign.Feign; import feign.Logger; import feign.RequestInterceptor; import feign.RequestTemplate; +import feign.Target; import feign.hystrix.FallbackFactory; +import feign.hystrix.SetterFactory; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -85,6 +91,7 @@ import rx.Single; /** * @author Spencer Gibb * @author Jakub Narloch + * @author Erik Kringen */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = FeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { @@ -124,6 +131,9 @@ public class FeignClientTests { @Qualifier("localapp3FeignClient") HystrixClient namedHystrixClient; + @Autowired + HystrixSetterFactoryClient hystrixSetterFactoryClient; + protected enum Arg { A, B; @@ -298,18 +308,47 @@ public class FeignClientTests { } } + @FeignClient(name = "localapp5", configuration = TestHystrixSetterFactoryClientConfig.class) + protected interface HystrixSetterFactoryClient { + @RequestMapping(method = RequestMethod.GET, path = "/hellos") + HystrixCommand> getHellosHystrix(); + } + + public static class TestHystrixSetterFactoryClientConfig { + public static final String SETTER_PREFIX = "SETTER-"; + @Bean + public SetterFactory commandKeyIsRequestLineSetterFactory() { + return new SetterFactory() { + @Override public HystrixCommand.Setter create(Target target, + Method method) { + String groupKey = SETTER_PREFIX + target.name(); + RequestMapping requestMapping = method + .getAnnotation(RequestMapping.class); + String commandKey = + SETTER_PREFIX + requestMapping.method()[0] + " " + requestMapping + .path()[0]; + return HystrixCommand.Setter + .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) + .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); + } + }; + } + } + @Configuration @EnableAutoConfiguration @RestController @EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class, - DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class }, - defaultConfiguration = TestDefaultFeignConfig.class) + DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class, + HystrixSetterFactoryClient.class}, + defaultConfiguration = TestDefaultFeignConfig.class) @RibbonClients({ - @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class) + @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp5", configuration = LocalRibbonClientConfiguration.class) }) protected static class Application { @@ -525,12 +564,16 @@ public class FeignClientTests { } @Test - public void testHystrixCommand() { + public void testHystrixCommand() throws NoSuchMethodException { HystrixCommand> command = this.testClient.getHellosHystrix(); assertNotNull("command was null", command); assertEquals( - "Hystrix command group name should match the name of the feign client", - "localapp", command.getCommandGroup().name()); + "Hystrix command group name should match the name of the feign client", + "localapp", command.getCommandGroup().name()); + String configKey = Feign.configKey(TestClient.class, + TestClient.class.getMethod("getHellosHystrix", (Class[]) null)); + assertEquals("Hystrix command key name should match the feign config key", + configKey, command.getCommandKey().name()); List hellos = command.execute(); assertNotNull("hellos was null", hellos); assertEquals("hellos didn't match", hellos, getHelloList()); @@ -658,6 +701,25 @@ public class FeignClientTests { assertNotNull("namedHystrixClient was null", this.namedHystrixClient); } + @Test + public void testHystrixSetterFactory() { + HystrixCommand> command = this.hystrixSetterFactoryClient + .getHellosHystrix(); + assertNotNull("command was null", command); + String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX; + assertEquals( + "Hystrix command group name should match the name of the feign client with a prefix of " + + setterPrefix, setterPrefix + "localapp5", + command.getCommandGroup().name()); + assertEquals( + "Hystrix command key name should match the request method (space) request path with a prefix of " + + setterPrefix, setterPrefix + "GET /hellos", + command.getCommandKey().name()); + List hellos = command.execute(); + assertNotNull("hellos was null", hellos); + assertEquals("hellos didn't match", hellos, getHelloList()); + } + @Data @AllArgsConstructor @NoArgsConstructor