Skip to content

Commit d890bd6

Browse files
kptfhskarpenko
andauthored
Fix support of "qualifier" parameter as feign client bean name (#642)
Fixes #611 Co-authored-by: skarpenko <[email protected]>
1 parent 7651d2c commit d890bd6

File tree

6 files changed

+162
-29
lines changed

6 files changed

+162
-29
lines changed

feign-reactor-spring-configuration/src/main/java/reactivefeign/spring/config/ReactiveFeignBasicConfigurator.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.List;
3434
import java.util.Map;
3535
import java.util.Objects;
36+
import java.util.Optional;
3637
import java.util.stream.Collectors;
3738

3839

@@ -48,7 +49,7 @@ public ReactiveFeignBuilder configure(
4849
ReactiveFeignBuilder builder,
4950
ReactiveFeignNamedContext namedContext) {
5051

51-
if (namedContext.getProperties().isDefaultToProperties()) {
52+
if (isDefaultToProperties(namedContext)) {
5253
builder = configureUsingConfiguration(builder, namedContext);
5354
for(ReactiveFeignClientsProperties.ReactiveFeignClientProperties<?> config : namedContext.getConfigsReverted()){
5455
builder = configureUsingProperties(builder, namedContext, config);
@@ -62,6 +63,13 @@ public ReactiveFeignBuilder configure(
6263
return builder;
6364
}
6465

66+
private boolean isDefaultToProperties(ReactiveFeignNamedContext namedContext){
67+
Map<String, ReactiveFeignClientsProperties.ReactiveFeignClientProperties<?>> config = namedContext.getProperties().getConfig();
68+
return Optional.ofNullable(config.get(namedContext.getClientName()))
69+
.map(ReactiveFeignClientsProperties.ReactiveFeignClientProperties::getDefaultToProperties)
70+
.orElse(namedContext.getProperties().isDefaultToProperties());
71+
}
72+
6573
private ReactiveFeignBuilder configureUsingConfiguration(ReactiveFeignBuilder builder, ReactiveFeignNamedContext namedContext) {
6674
ReactiveFeignBuilder resultBuilder = builder;
6775

feign-reactor-spring-configuration/src/main/java/reactivefeign/spring/config/ReactiveFeignClientsProperties.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import reactivefeign.client.statushandler.ReactiveStatusHandler;
1111
import reactivefeign.retry.ReactiveRetryPolicy;
1212

13-
import java.util.Collection;
1413
import java.util.HashMap;
1514
import java.util.List;
1615
import java.util.Map;
@@ -65,6 +64,8 @@ public int hashCode() {
6564

6665
public static class ReactiveFeignClientProperties<O extends ReactiveOptions.Builder> {
6766

67+
private Boolean defaultToProperties;
68+
6869
private O options;
6970

7071
//used for no cloud configuration
@@ -92,6 +93,14 @@ public static class ReactiveFeignClientProperties<O extends ReactiveOptions.Buil
9293

9394
private Map<String, List<String>> defaultQueryParameters;
9495

96+
public Boolean getDefaultToProperties() {
97+
return defaultToProperties;
98+
}
99+
100+
public void setDefaultToProperties(Boolean defaultToProperties) {
101+
this.defaultToProperties = defaultToProperties;
102+
}
103+
95104
public O getOptions() {
96105
return options;
97106
}
@@ -193,7 +202,8 @@ public boolean equals(Object o) {
193202
if (this == o) return true;
194203
if (o == null || getClass() != o.getClass()) return false;
195204
ReactiveFeignClientProperties that = (ReactiveFeignClientProperties) o;
196-
return Objects.equals(options, that.options) &&
205+
return Objects.equals(defaultToProperties, that.defaultToProperties) &&
206+
Objects.equals(options, that.options) &&
197207
Objects.equals(retryOnSame, that.retryOnSame) &&
198208
Objects.equals(retryOnNext, that.retryOnNext) &&
199209
Objects.equals(statusHandler, that.statusHandler) &&

feign-reactor-spring-configuration/src/main/java/reactivefeign/spring/config/ReactiveFeignClientsRegistrar.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import org.springframework.core.type.filter.TypeFilter;
4141
import org.springframework.util.Assert;
4242
import org.springframework.util.ClassUtils;
43-
import org.springframework.util.StringUtils;
4443

4544
import java.io.IOException;
4645
import java.net.MalformedURLException;
@@ -53,6 +52,7 @@
5352
import java.util.Map;
5453
import java.util.Set;
5554

55+
import static org.springframework.util.StringUtils.hasText;
5656
import static reactivefeign.utils.StringUtils.cutTail;
5757

5858
/**
@@ -148,9 +148,11 @@ protected boolean match(ClassMetadata metadata) {
148148
.getAnnotationAttributes(
149149
ReactiveFeignClient.class.getCanonicalName());
150150

151-
String name = getClientName(attributes);
152-
registerClientConfiguration(registry, name,
153-
attributes.get("configuration"));
151+
String name = getQualifier(attributes);
152+
if(!hasText(name)){
153+
name = getClientName(attributes);
154+
}
155+
registerClientConfiguration(registry, name, attributes.get("configuration"));
154156

155157
registerReactiveFeignClient(registry, annotationMetadata, attributes);
156158
}
@@ -183,7 +185,7 @@ private void registerReactiveFeignClient(BeanDefinitionRegistry registry,
183185
beanDefinition.setPrimary(primary);
184186

185187
String qualifier = getQualifier(attributes);
186-
if (StringUtils.hasText(qualifier)) {
188+
if (hasText(qualifier)) {
187189
alias = qualifier;
188190
}
189191

@@ -215,18 +217,18 @@ static void validateFallbackFactory(final Class clazz) {
215217

216218
/* for testing */ String getName(Map<String, Object> attributes) {
217219
String name = (String) attributes.get("serviceId");
218-
if (!StringUtils.hasText(name)) {
220+
if (!hasText(name)) {
219221
name = (String) attributes.get("name");
220222
}
221-
if (!StringUtils.hasText(name)) {
223+
if (!hasText(name)) {
222224
name = (String) attributes.get("value");
223225
}
224226
name = resolve(name);
225227
return getName(name);
226228
}
227229

228230
static String getName(String name) {
229-
if (!StringUtils.hasText(name)) {
231+
if (!hasText(name)) {
230232
return "";
231233
}
232234

@@ -248,7 +250,7 @@ static String getName(String name) {
248250
}
249251

250252
private String resolve(String value) {
251-
if (StringUtils.hasText(value)) {
253+
if (hasText(value)) {
252254
return this.environment.resolvePlaceholders(value);
253255
}
254256
return value;
@@ -260,7 +262,7 @@ private String getUrl(Map<String, Object> attributes) {
260262
}
261263

262264
static String getUrl(String url) {
263-
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
265+
if (hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
264266
if (!url.contains("://")) {
265267
url = "http://" + url;
266268
}
@@ -280,7 +282,7 @@ private String getPath(Map<String, Object> attributes) {
280282
}
281283

282284
static String getPath(String path) {
283-
if (StringUtils.hasText(path)) {
285+
if (hasText(path)) {
284286
path = path.trim();
285287
if (!path.startsWith("/")) {
286288
path = "/" + path;
@@ -311,12 +313,12 @@ protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata)
311313

312314
Set<String> basePackages = new HashSet<>();
313315
for (String pkg : (String[]) attributes.get("value")) {
314-
if (StringUtils.hasText(pkg)) {
316+
if (hasText(pkg)) {
315317
basePackages.add(pkg);
316318
}
317319
}
318320
for (String pkg : (String[]) attributes.get("basePackages")) {
319-
if (StringUtils.hasText(pkg)) {
321+
if (hasText(pkg)) {
320322
basePackages.add(pkg);
321323
}
322324
}
@@ -336,7 +338,7 @@ private String getQualifier(Map<String, Object> client) {
336338
return null;
337339
}
338340
String qualifier = (String) client.get("qualifier");
339-
if (StringUtils.hasText(qualifier)) {
341+
if (hasText(qualifier)) {
340342
return qualifier;
341343
}
342344
return null;
@@ -347,13 +349,13 @@ private String getClientName(Map<String, Object> client) {
347349
return null;
348350
}
349351
String value = (String) client.get("value");
350-
if (!StringUtils.hasText(value)) {
352+
if (!hasText(value)) {
351353
value = (String) client.get("name");
352354
}
353-
if (!StringUtils.hasText(value)) {
355+
if (!hasText(value)) {
354356
value = (String) client.get("serviceId");
355357
}
356-
if (StringUtils.hasText(value)) {
358+
if (hasText(value)) {
357359
return value;
358360
}
359361

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package reactivefeign.spring.config.cloud2;
2+
3+
import com.github.tomakehurst.wiremock.WireMockServer;
4+
import org.junit.AfterClass;
5+
import org.junit.Before;
6+
import org.junit.BeforeClass;
7+
import org.junit.Test;
8+
import org.junit.runner.RunWith;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
11+
import org.springframework.boot.test.context.SpringBootTest;
12+
import org.springframework.context.annotation.Configuration;
13+
import org.springframework.test.annotation.DirtiesContext;
14+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.RequestMethod;
17+
import reactivefeign.spring.config.EnableReactiveFeignClients;
18+
import reactivefeign.spring.config.ReactiveFeignClient;
19+
import reactor.core.publisher.Mono;
20+
import reactor.test.StepVerifier;
21+
22+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
23+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
24+
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
25+
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
26+
import static java.util.Arrays.asList;
27+
import static reactivefeign.spring.config.cloud2.BasicAutoconfigurationTest.MOCK_SERVER_PORT_PROPERTY;
28+
import static reactivefeign.spring.config.cloud2.QualifierTest.RFGN_PROPS;
29+
30+
@RunWith(SpringJUnit4ClassRunner.class)
31+
@SpringBootTest(classes = QualifierTest.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE,
32+
properties = {
33+
"spring.cloud.discovery.client.simple.instances."+RFGN_PROPS+"[0].uri=http://localhost:${"+ MOCK_SERVER_PORT_PROPERTY+"}",
34+
"reactive.feign.client.config.default.options.readTimeoutMillis=100",
35+
"reactive.feign.client.config.rfgn-proper.options.readTimeoutMillis=500"
36+
})
37+
@DirtiesContext
38+
public class QualifierTest extends BasicAutoconfigurationTest{
39+
40+
public static final String RFGN_PROPS = "rfgn-proper";
41+
42+
private static final WireMockServer mockHttpServer = new WireMockServer(wireMockConfig().dynamicPort());
43+
44+
@Autowired
45+
private PropsSampleClient propsSampleClient;
46+
47+
@Autowired
48+
private PropsSampleClientWithOtherQualifier propsSampleClientWithOtherQualifier;
49+
50+
51+
@Test
52+
public void shouldRunClientsWithSameNameAndDifferentQualifiers() {
53+
mockHttpServer.stubFor(get(urlPathMatching("/sampleUrl"))
54+
.willReturn(aResponse()
55+
.withFixedDelay(300)
56+
.withBody("OK")));
57+
58+
asList(propsSampleClient, propsSampleClientWithOtherQualifier).forEach(feignClient -> {
59+
StepVerifier.create(feignClient.sampleMethod())
60+
.expectNext("OK")
61+
.verifyComplete();
62+
});
63+
}
64+
65+
@ReactiveFeignClient(name = RFGN_PROPS, qualifier = "FeignClient1")
66+
protected interface PropsSampleClient extends SampleClient {
67+
68+
@RequestMapping(method = RequestMethod.GET, value = "/sampleUrl")
69+
Mono<String> sampleMethod();
70+
}
71+
72+
@ReactiveFeignClient(name = RFGN_PROPS, qualifier = "FeignClient2")
73+
protected interface PropsSampleClientWithOtherQualifier extends SampleClient {
74+
75+
@RequestMapping(method = RequestMethod.GET, value = "/sampleUrl")
76+
Mono<String> sampleMethod();
77+
}
78+
79+
protected interface SampleClient{
80+
Mono<String> sampleMethod();
81+
}
82+
83+
@Configuration
84+
@EnableAutoConfiguration
85+
@EnableReactiveFeignClients(
86+
clients = {PropsSampleClient.class,
87+
PropsSampleClientWithOtherQualifier.class})
88+
protected static class TestConfiguration {
89+
}
90+
91+
@BeforeClass
92+
public static void setupStubs() {
93+
mockHttpServer.start();
94+
95+
System.setProperty(MOCK_SERVER_PORT_PROPERTY, Integer.toString(mockHttpServer.port()));
96+
}
97+
98+
@Before
99+
public void reset() throws InterruptedException {
100+
//to close circuit breaker
101+
mockHttpServer.resetAll();
102+
}
103+
104+
@AfterClass
105+
public static void teardown() {
106+
mockHttpServer.stop();
107+
}
108+
}

feign-reactor-test/feign-reactor-spring-configuration-cloud2-test/src/test/java/reactivefeign/spring/config/cloud2/SampleConfigurationsTest.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public class SampleConfigurationsTest extends BasicAutoconfigurationTest{
9191
public static final int UPDATE_INTERVAL = 5;
9292
public static final int SLEEP_WINDOW = 1000;
9393

94-
private static WireMockServer mockHttpServer = new WireMockServer(wireMockConfig().dynamicPort());
94+
private static final WireMockServer mockHttpServer = new WireMockServer(wireMockConfig().dynamicPort());
9595

9696
//configured via properties file
9797
@Autowired
@@ -107,7 +107,7 @@ public class SampleConfigurationsTest extends BasicAutoconfigurationTest{
107107
@Autowired
108108
private ErrorDecoderSampleClient errorDecoderSampleClient;
109109

110-
private static CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
110+
private static final CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
111111

112112
//this test checks that default readTimeoutMillis is overridden for each client
113113
// (one in via properties file and other via configuration class)
@@ -119,7 +119,7 @@ public void shouldRetryAndFailOnRibbon() {
119119
.withBody("OK")));
120120

121121
asList(propertiesSampleClient, configsSampleClient).forEach(feignClient -> {
122-
StepVerifier.create(propertiesSampleClient.sampleMethod())
122+
StepVerifier.create(feignClient.sampleMethod())
123123
.expectErrorMatches(throwable ->
124124
throwable instanceof RuntimeException
125125
&& throwable.getCause() instanceof OutOfRetriesException
@@ -204,34 +204,37 @@ public void shouldOpenCircuitBreakerButNotWrapException() throws InterruptedExce
204204
}
205205

206206
@ReactiveFeignClient(name = RFGN_PROPER)
207-
protected interface PropertiesSampleClient {
207+
protected interface PropertiesSampleClient extends SampleClient{
208208

209209
@RequestMapping(method = RequestMethod.GET, value = "/sampleUrl")
210210
Mono<String> sampleMethod();
211211
}
212212

213213
@ReactiveFeignClient(name = RFGN_CONFIGS,
214214
configuration = ReactiveFeignSampleConfiguration.class)
215-
protected interface ConfigsSampleClient {
215+
protected interface ConfigsSampleClient extends SampleClient{
216216

217217
@RequestMapping(method = RequestMethod.GET, value = "/sampleUrl")
218218
Mono<String> sampleMethod();
219219
}
220-
221220
@ReactiveFeignClient(name = RFGN_FALLBACK,
222221
fallback = ReactiveFeignFallbackConfiguration.Fallback.class,
223222
configuration = ReactiveFeignFallbackConfiguration.class)
224-
protected interface FallbackSampleClient {
223+
protected interface FallbackSampleClient extends SampleClient{
225224
@RequestMapping(method = RequestMethod.GET, value = "/sampleUrl")
226225
Mono<String> sampleMethod();
227226
}
228227

229228
@ReactiveFeignClient(name = RFGN_ERRORDECODER, fallbackFactory = ErrorDecoderSkipFallbackFactory.class)
230-
protected interface ErrorDecoderSampleClient {
229+
protected interface ErrorDecoderSampleClient extends SampleClient{
231230
@RequestMapping(method = RequestMethod.GET, value = "/sampleUrl")
232231
Mono<String> sampleMethod();
233232
}
234233

234+
protected interface SampleClient{
235+
Mono<String> sampleMethod();
236+
}
237+
235238
protected static class ErrorDecoderSkipFallbackFactory implements FallbackFactory<ErrorDecoderSampleClient> {
236239

237240
@Override
@@ -297,7 +300,7 @@ protected static class ReactiveFeignSampleConfiguration {
297300

298301
@Bean
299302
public ReactiveOptions reactiveOptions(){
300-
return new WebReactiveOptions.Builder().setReadTimeoutMillis(500).build();
303+
return new WebReactiveOptions.Builder().setReadTimeoutMillis(300).build();
301304
}
302305

303306
@Bean

feign-reactor-test/feign-reactor-spring-configuration-cloud2-test/src/test/resources/error-decoder.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ reactive.feign.client.config.rfgn-proper.retryOnSame.builder=reactivefeign.retry
55
reactive.feign.client.config.rfgn-proper.retryOnSame.args.maxRetries=1
66
reactive.feign.client.config.rfgn-proper.retryOnSame.args.backoffInMs=10
77

8+
reactive.feign.client.config.rfgn-configs.defaultToProperties=false
9+
810
reactive.feign.client.config.rfgn-errordecoder.errorDecoder=reactivefeign.spring.config.cloud2.ErrorDecoder

0 commit comments

Comments
 (0)