Commit d78da7b9 by Erik Kringen Committed by Spencer Gibb

Add support for Feign.SetterFactory (#1673)

Fixes gh-1414
parent c795bd16
...@@ -976,6 +976,7 @@ Spring Cloud Netflix _does not_ provide the following beans by default for feign ...@@ -976,6 +976,7 @@ Spring Cloud Netflix _does not_ provide the following beans by default for feign
* `ErrorDecoder` * `ErrorDecoder`
* `Request.Options` * `Request.Options`
* `Collection<RequestInterceptor>` * `Collection<RequestInterceptor>`
* `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: 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:
......
...@@ -17,14 +17,17 @@ ...@@ -17,14 +17,17 @@
package org.springframework.cloud.netflix.feign; package org.springframework.cloud.netflix.feign;
import org.springframework.util.Assert;
import feign.Feign; import feign.Feign;
import feign.Target; import feign.Target;
import feign.hystrix.FallbackFactory; import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign; import feign.hystrix.HystrixFeign;
import org.springframework.util.Assert; import feign.hystrix.SetterFactory;
/** /**
* @author Spencer Gibb * @author Spencer Gibb
* @author Erik Kringen
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
class HystrixTargeter implements Targeter { class HystrixTargeter implements Targeter {
...@@ -36,6 +39,11 @@ class HystrixTargeter implements Targeter { ...@@ -36,6 +39,11 @@ class HystrixTargeter implements Targeter {
return feign.target(target); return feign.target(target);
} }
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; 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(); Class<?> fallback = factory.getFallback();
if (fallback != void.class) { if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder, fallback); return targetWithFallback(factory.getName(), context, target, builder, fallback);
...@@ -95,4 +103,9 @@ class HystrixTargeter implements Targeter { ...@@ -95,4 +103,9 @@ class HystrixTargeter implements Targeter {
} }
return (T) fallbackInstance; return (T) fallbackInstance;
} }
private <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
} }
...@@ -25,6 +25,7 @@ import static org.junit.Assert.assertThat; ...@@ -25,6 +25,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -68,14 +69,19 @@ import org.springframework.web.bind.annotation.RequestParam; ...@@ -68,14 +69,19 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList; import com.netflix.loadbalancer.ServerList;
import feign.Client; import feign.Client;
import feign.Feign;
import feign.Logger; import feign.Logger;
import feign.RequestInterceptor; import feign.RequestInterceptor;
import feign.RequestTemplate; import feign.RequestTemplate;
import feign.Target;
import feign.hystrix.FallbackFactory; import feign.hystrix.FallbackFactory;
import feign.hystrix.SetterFactory;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
...@@ -85,6 +91,7 @@ import rx.Single; ...@@ -85,6 +91,7 @@ import rx.Single;
/** /**
* @author Spencer Gibb * @author Spencer Gibb
* @author Jakub Narloch * @author Jakub Narloch
* @author Erik Kringen
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { @SpringBootTest(classes = FeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
...@@ -124,6 +131,9 @@ public class FeignClientTests { ...@@ -124,6 +131,9 @@ public class FeignClientTests {
@Qualifier("localapp3FeignClient") @Qualifier("localapp3FeignClient")
HystrixClient namedHystrixClient; HystrixClient namedHystrixClient;
@Autowired
HystrixSetterFactoryClient hystrixSetterFactoryClient;
protected enum Arg { protected enum Arg {
A, B; A, B;
...@@ -298,18 +308,47 @@ public class FeignClientTests { ...@@ -298,18 +308,47 @@ public class FeignClientTests {
} }
} }
@FeignClient(name = "localapp5", configuration = TestHystrixSetterFactoryClientConfig.class)
protected interface HystrixSetterFactoryClient {
@RequestMapping(method = RequestMethod.GET, path = "/hellos")
HystrixCommand<List<Hello>> 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 @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration
@RestController @RestController
@EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class, @EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class,
DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class }, DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class,
HystrixSetterFactoryClient.class},
defaultConfiguration = TestDefaultFeignConfig.class) defaultConfiguration = TestDefaultFeignConfig.class)
@RibbonClients({ @RibbonClients({
@RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class) @RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class),
@RibbonClient(name = "localapp5", configuration = LocalRibbonClientConfiguration.class)
}) })
protected static class Application { protected static class Application {
...@@ -525,12 +564,16 @@ public class FeignClientTests { ...@@ -525,12 +564,16 @@ public class FeignClientTests {
} }
@Test @Test
public void testHystrixCommand() { public void testHystrixCommand() throws NoSuchMethodException {
HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix(); HystrixCommand<List<Hello>> command = this.testClient.getHellosHystrix();
assertNotNull("command was null", command); assertNotNull("command was null", command);
assertEquals( assertEquals(
"Hystrix command group name should match the name of the feign client", "Hystrix command group name should match the name of the feign client",
"localapp", command.getCommandGroup().name()); "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<Hello> hellos = command.execute(); List<Hello> hellos = command.execute();
assertNotNull("hellos was null", hellos); assertNotNull("hellos was null", hellos);
assertEquals("hellos didn't match", hellos, getHelloList()); assertEquals("hellos didn't match", hellos, getHelloList());
...@@ -658,6 +701,25 @@ public class FeignClientTests { ...@@ -658,6 +701,25 @@ public class FeignClientTests {
assertNotNull("namedHystrixClient was null", this.namedHystrixClient); assertNotNull("namedHystrixClient was null", this.namedHystrixClient);
} }
@Test
public void testHystrixSetterFactory() {
HystrixCommand<List<Hello>> 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<Hello> hellos = command.execute();
assertNotNull("hellos was null", hellos);
assertEquals("hellos didn't match", hellos, getHelloList());
}
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment