Skip to content

Commit dc15910

Browse files
artembilangaryrussell
authored andcommitted
GH-3266: Don't override props in AnnGWProxyFB
Fixes #3266 It turns out that `AnnotationGatewayProxyFactoryBean.onInit()` implementation parses a `@MessagingGateway` attributes ignoring possible properties population by setters. This way a Java DSL `GatewayProxySpec` becomes useless since all its options are overridden by default values from a synthesized `@MessagingGateway`. Also it is inconsistency when we declare an `AnnotationGatewayProxyFactoryBean` as regular bean, but then called setters are ignored * Add `protected` getters into `GatewayProxyFactoryBean` for all the properties which can be overridden by annotation attributes * Fix `AnnotationGatewayProxyFactoryBean` to consult with those getters before populating a property with value from the annotation **Cherry-pick to 5.2.x**
1 parent 9ec6529 commit dc15910

File tree

3 files changed

+125
-36
lines changed

3 files changed

+125
-36
lines changed

spring-integration-core/src/main/java/org/springframework/integration/gateway/AnnotationGatewayProxyFactoryBean.java

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2017-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -72,23 +72,38 @@ public AnnotationGatewayProxyFactoryBean(Class<?> serviceInterface) {
7272
protected void onInit() {
7373
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) getBeanFactory();
7474

75-
populateGatewayMethodMetadata();
75+
if (getGlobalMethodMetadata() == null) {
76+
populateGatewayMethodMetadata();
77+
}
78+
79+
String defaultRequestTimeout = resolveAttribute("defaultRequestTimeout");
80+
String defaultReplyTimeout = resolveAttribute("defaultReplyTimeout");
7681

7782
JavaUtils.INSTANCE
78-
.acceptIfHasText(resolveAttribute("defaultRequestChannel"), this::setDefaultRequestChannelName)
79-
.acceptIfHasText(resolveAttribute("defaultReplyChannel"), this::setDefaultReplyChannelName)
80-
.acceptIfHasText(resolveAttribute("errorChannel"), this::setErrorChannelName)
81-
.acceptIfHasText(resolveAttribute("defaultRequestTimeout"),
83+
.acceptIfCondition(getDefaultRequestChannel() == null && getDefaultRequestChannelName() == null,
84+
resolveAttribute("defaultRequestChannel"),
85+
this::setDefaultRequestChannelName)
86+
.acceptIfCondition(getDefaultReplyChannel() == null && getDefaultReplyChannelName() == null,
87+
resolveAttribute("defaultReplyChannel"),
88+
this::setDefaultReplyChannelName)
89+
.acceptIfCondition(getErrorChannel() == null && getErrorChannelName() == null,
90+
resolveAttribute("errorChannel"),
91+
this::setErrorChannelName)
92+
.acceptIfCondition(getDefaultRequestTimeout() == null && StringUtils.hasText(defaultRequestTimeout),
93+
defaultRequestTimeout,
8294
value -> setDefaultRequestTimeout(Long.parseLong(value)))
83-
.acceptIfHasText(resolveAttribute("defaultReplyTimeout"),
95+
.acceptIfCondition(getDefaultReplyTimeout() == null && StringUtils.hasText(defaultReplyTimeout),
96+
defaultReplyTimeout,
8497
value -> setDefaultReplyTimeout(Long.parseLong(value)));
8598

86-
String asyncExecutor = beanFactory.resolveEmbeddedValue(this.gatewayAttributes.getString("asyncExecutor"));
87-
if (asyncExecutor == null || AnnotationConstants.NULL.equals(asyncExecutor)) {
88-
setAsyncExecutor(null);
89-
}
90-
else if (StringUtils.hasText(asyncExecutor)) {
91-
setAsyncExecutor(beanFactory.getBean(asyncExecutor, Executor.class));
99+
if (!isAsyncExecutorExplicitlySet()) {
100+
String asyncExecutor = resolveAttribute("asyncExecutor");
101+
if (asyncExecutor == null || AnnotationConstants.NULL.equals(asyncExecutor)) {
102+
setAsyncExecutor(null);
103+
}
104+
else if (StringUtils.hasText(asyncExecutor)) {
105+
setAsyncExecutor(beanFactory.getBean(asyncExecutor, Executor.class));
106+
}
92107
}
93108
boolean proxyDefaultMethods = this.gatewayAttributes.getBoolean("proxyDefaultMethods");
94109
if (proxyDefaultMethods) {
@@ -117,7 +132,7 @@ private void populateGatewayMethodMetadata() {
117132
"'defaultHeaders' are not allowed when a 'mapper' is provided");
118133

119134
JavaUtils.INSTANCE
120-
.acceptIfHasText(mapper,
135+
.acceptIfCondition(hasMapper && getMapper() == null, mapper,
121136
value -> setMapper(beanFactory.getBean(value, MethodArgsMessageMapper.class)));
122137

123138
if (hasDefaultHeaders || hasDefaultPayloadExpression) {
@@ -127,23 +142,26 @@ private void populateGatewayMethodMetadata() {
127142
gatewayMethodMetadata.setPayloadExpression(EXPRESSION_PARSER.parseExpression(defaultPayloadExpression));
128143
}
129144

130-
Map<String, Expression> headerExpressions = Arrays.stream(defaultHeaders)
131-
.collect(Collectors.toMap(
132-
header -> beanFactory.resolveEmbeddedValue((String) header.get("name")),
133-
header -> {
134-
String headerValue = beanFactory.resolveEmbeddedValue((String) header.get("value"));
135-
boolean hasValue = StringUtils.hasText(headerValue);
136-
137-
String headerExpression =
138-
beanFactory.resolveEmbeddedValue((String) header.get("expression"));
139-
140-
Assert.state(!(hasValue == StringUtils.hasText(headerExpression)),
141-
"exactly one of 'value' or 'expression' is required on a gateway's header.");
142-
143-
return hasValue ?
144-
new LiteralExpression(headerValue) :
145-
EXPRESSION_PARSER.parseExpression(headerExpression);
146-
}));
145+
Map<String, Expression> headerExpressions =
146+
Arrays.stream(defaultHeaders)
147+
.collect(Collectors.toMap(
148+
header -> beanFactory.resolveEmbeddedValue((String) header.get("name")),
149+
header -> {
150+
String headerValue =
151+
beanFactory.resolveEmbeddedValue((String) header.get("value"));
152+
boolean hasValue = StringUtils.hasText(headerValue);
153+
154+
String headerExpression =
155+
beanFactory.resolveEmbeddedValue((String) header.get("expression"));
156+
157+
Assert.state(!(hasValue == StringUtils.hasText(headerExpression)),
158+
"exactly one of 'value' or 'expression' is required on a gateway's " +
159+
"header.");
160+
161+
return hasValue ?
162+
new LiteralExpression(headerValue) :
163+
EXPRESSION_PARSER.parseExpression(headerExpression);
164+
}));
147165

148166
gatewayMethodMetadata.setHeaderExpressions(headerExpressions);
149167

spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
139139

140140
private AsyncTaskExecutor asyncExecutor = new SimpleAsyncTaskExecutor();
141141

142+
private boolean asyncExecutorExplicitlySet;
143+
142144
private Class<?> asyncSubmitType;
143145

144146
private Class<?> asyncSubmitListenableType;
@@ -189,6 +191,16 @@ public void setDefaultRequestChannelName(String defaultRequestChannelName) {
189191
this.defaultRequestChannelName = defaultRequestChannelName;
190192
}
191193

194+
@Nullable
195+
protected MessageChannel getDefaultRequestChannel() {
196+
return this.defaultRequestChannel;
197+
}
198+
199+
@Nullable
200+
protected String getDefaultRequestChannelName() {
201+
return this.defaultRequestChannelName;
202+
}
203+
192204
/**
193205
* Set the default reply channel. If no default reply channel is provided,
194206
* and no reply channel is configured with annotations, an anonymous,
@@ -212,6 +224,16 @@ public void setDefaultReplyChannelName(String defaultReplyChannelName) {
212224
this.defaultReplyChannelName = defaultReplyChannelName;
213225
}
214226

227+
@Nullable
228+
protected MessageChannel getDefaultReplyChannel() {
229+
return this.defaultReplyChannel;
230+
}
231+
232+
@Nullable
233+
protected String getDefaultReplyChannelName() {
234+
return this.defaultReplyChannelName;
235+
}
236+
215237
/**
216238
* Set the error channel. If no error channel is provided, this gateway will
217239
* propagate Exceptions to the caller. To completely suppress Exceptions, provide
@@ -233,6 +255,16 @@ public void setErrorChannelName(String errorChannelName) {
233255
this.errorChannelName = errorChannelName;
234256
}
235257

258+
@Nullable
259+
protected MessageChannel getErrorChannel() {
260+
return this.errorChannel;
261+
}
262+
263+
@Nullable
264+
protected String getErrorChannelName() {
265+
return this.errorChannelName;
266+
}
267+
236268
/**
237269
* Set the default timeout value for sending request messages. If not explicitly
238270
* configured with an annotation, or on a method element, this value will be used.
@@ -266,6 +298,11 @@ public void setDefaultRequestTimeoutExpressionString(String defaultRequestTimeou
266298
}
267299
}
268300

301+
@Nullable
302+
protected Expression getDefaultRequestTimeout() {
303+
return this.defaultRequestTimeout;
304+
}
305+
269306
/**
270307
* Set the default timeout value for receiving reply messages. If not explicitly
271308
* configured with an annotation, or on a method element, this value will be used.
@@ -299,6 +336,11 @@ public void setDefaultReplyTimeoutExpressionString(String defaultReplyTimeout) {
299336
}
300337
}
301338

339+
@Nullable
340+
protected Expression getDefaultReplyTimeout() {
341+
return this.defaultReplyTimeout;
342+
}
343+
302344
@Override
303345
public void setShouldTrack(boolean shouldTrack) {
304346
this.shouldTrack = shouldTrack;
@@ -317,12 +359,15 @@ public void setShouldTrack(boolean shouldTrack) {
317359
* @param executor The executor.
318360
*/
319361
public void setAsyncExecutor(@Nullable Executor executor) {
320-
if (executor == null && logger.isInfoEnabled()) {
362+
if (executor == null) {
321363
logger.info("A null executor disables the async gateway; " +
322364
"methods returning Future<?> will run on the calling thread");
323365
}
324-
this.asyncExecutor = (executor instanceof AsyncTaskExecutor || executor == null) ? (AsyncTaskExecutor) executor
325-
: new TaskExecutorAdapter(executor);
366+
this.asyncExecutor =
367+
(executor instanceof AsyncTaskExecutor || executor == null)
368+
? (AsyncTaskExecutor) executor
369+
: new TaskExecutorAdapter(executor);
370+
this.asyncExecutorExplicitlySet = true;
326371
}
327372

328373
public void setTypeConverter(TypeConverter typeConverter) {
@@ -338,6 +383,11 @@ public void setGlobalMethodMetadata(GatewayMethodMetadata globalMethodMetadata)
338383
this.globalMethodMetadata = globalMethodMetadata;
339384
}
340385

386+
@Nullable
387+
protected GatewayMethodMetadata getGlobalMethodMetadata() {
388+
return this.globalMethodMetadata;
389+
}
390+
341391
@Override
342392
public void setBeanClassLoader(ClassLoader beanClassLoader) {
343393
this.beanClassLoader = beanClassLoader;
@@ -352,6 +402,11 @@ public final void setMapper(MethodArgsMessageMapper mapper) {
352402
this.argsMapper = mapper;
353403
}
354404

405+
@Nullable
406+
protected MethodArgsMessageMapper getMapper() {
407+
return this.argsMapper;
408+
}
409+
355410
/**
356411
* Indicate if {@code default} methods on the interface should be proxied as well.
357412
* If an explicit {@link Gateway} annotation is present on method it is proxied
@@ -365,10 +420,15 @@ public void setProxyDefaultMethods(boolean proxyDefaultMethods) {
365420
this.proxyDefaultMethods = proxyDefaultMethods;
366421
}
367422

423+
@Nullable
368424
protected AsyncTaskExecutor getAsyncExecutor() {
369425
return this.asyncExecutor;
370426
}
371427

428+
protected boolean isAsyncExecutorExplicitlySet() {
429+
return this.asyncExecutorExplicitlySet;
430+
}
431+
372432
/**
373433
* Return the Map of {@link Method} to {@link MessagingGatewaySupport}
374434
* generated by this factory bean.

spring-integration-core/src/test/java/org/springframework/integration/dsl/gateway/GatewayDslTests.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019 the original author or authors.
2+
* Copyright 2019-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,13 +37,15 @@
3737
import org.springframework.integration.annotation.Gateway;
3838
import org.springframework.integration.channel.QueueChannel;
3939
import org.springframework.integration.config.EnableIntegration;
40+
import org.springframework.integration.core.MessagingTemplate;
4041
import org.springframework.integration.dsl.IntegrationFlow;
4142
import org.springframework.integration.dsl.IntegrationFlows;
4243
import org.springframework.integration.dsl.MessageChannels;
4344
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
4445
import org.springframework.integration.gateway.MessagingGatewaySupport;
4546
import org.springframework.integration.gateway.MethodArgsHolder;
4647
import org.springframework.integration.support.MessageBuilder;
48+
import org.springframework.integration.test.util.TestUtils;
4749
import org.springframework.messaging.Message;
4850
import org.springframework.messaging.MessageChannel;
4951
import org.springframework.messaging.PollableChannel;
@@ -139,6 +141,13 @@ void testHeadersFromFunctionGateway() {
139141
assertThat(receive).isNotNull()
140142
.extracting(Message::getPayload)
141143
.isEqualTo(defaultMethodPayload);
144+
145+
MessagingGatewaySupport methodGateway = gateways.values().iterator().next();
146+
MessagingTemplate messagingTemplate =
147+
TestUtils.getPropertyValue(methodGateway, "messagingTemplate", MessagingTemplate.class);
148+
149+
assertThat(messagingTemplate.getReceiveTimeout()).isEqualTo(10);
150+
assertThat(messagingTemplate.getSendTimeout()).isEqualTo(20);
142151
}
143152

144153
@Autowired
@@ -194,7 +203,9 @@ public IntegrationFlow functionGateway() {
194203
return IntegrationFlows.from(MessageFunction.class,
195204
(gateway) -> gateway
196205
.header("gatewayMethod", MethodArgsHolder::getMethod)
197-
.header("gatewayArgs", MethodArgsHolder::getArgs))
206+
.header("gatewayArgs", MethodArgsHolder::getArgs)
207+
.replyTimeout(10)
208+
.requestTimeout(20))
198209
.bridge()
199210
.get();
200211
}

0 commit comments

Comments
 (0)