GH-3266: Don't override props in AnnGWProxyFB
Fixes https://github.com/spring-projects/spring-integration/issues/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**
This commit is contained in:
committed by
Gary Russell
parent
000632e298
commit
2a79b07093
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 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.
|
||||
@@ -72,23 +72,38 @@ public class AnnotationGatewayProxyFactoryBean extends GatewayProxyFactoryBean {
|
||||
protected void onInit() {
|
||||
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory) getBeanFactory();
|
||||
|
||||
populateGatewayMethodMetadata();
|
||||
if (getGlobalMethodMetadata() == null) {
|
||||
populateGatewayMethodMetadata();
|
||||
}
|
||||
|
||||
String defaultRequestTimeout = resolveAttribute("defaultRequestTimeout");
|
||||
String defaultReplyTimeout = resolveAttribute("defaultReplyTimeout");
|
||||
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfHasText(resolveAttribute("defaultRequestChannel"), this::setDefaultRequestChannelName)
|
||||
.acceptIfHasText(resolveAttribute("defaultReplyChannel"), this::setDefaultReplyChannelName)
|
||||
.acceptIfHasText(resolveAttribute("errorChannel"), this::setErrorChannelName)
|
||||
.acceptIfHasText(resolveAttribute("defaultRequestTimeout"),
|
||||
.acceptIfCondition(getDefaultRequestChannel() == null && getDefaultRequestChannelName() == null,
|
||||
resolveAttribute("defaultRequestChannel"),
|
||||
this::setDefaultRequestChannelName)
|
||||
.acceptIfCondition(getDefaultReplyChannel() == null && getDefaultReplyChannelName() == null,
|
||||
resolveAttribute("defaultReplyChannel"),
|
||||
this::setDefaultReplyChannelName)
|
||||
.acceptIfCondition(getErrorChannel() == null && getErrorChannelName() == null,
|
||||
resolveAttribute("errorChannel"),
|
||||
this::setErrorChannelName)
|
||||
.acceptIfCondition(getDefaultRequestTimeout() == null && StringUtils.hasText(defaultRequestTimeout),
|
||||
defaultRequestTimeout,
|
||||
value -> setDefaultRequestTimeout(Long.parseLong(value)))
|
||||
.acceptIfHasText(resolveAttribute("defaultReplyTimeout"),
|
||||
.acceptIfCondition(getDefaultReplyTimeout() == null && StringUtils.hasText(defaultReplyTimeout),
|
||||
defaultReplyTimeout,
|
||||
value -> setDefaultReplyTimeout(Long.parseLong(value)));
|
||||
|
||||
String asyncExecutor = beanFactory.resolveEmbeddedValue(this.gatewayAttributes.getString("asyncExecutor"));
|
||||
if (asyncExecutor == null || AnnotationConstants.NULL.equals(asyncExecutor)) {
|
||||
setAsyncExecutor(null);
|
||||
}
|
||||
else if (StringUtils.hasText(asyncExecutor)) {
|
||||
setAsyncExecutor(beanFactory.getBean(asyncExecutor, Executor.class));
|
||||
if (!isAsyncExecutorExplicitlySet()) {
|
||||
String asyncExecutor = resolveAttribute("asyncExecutor");
|
||||
if (asyncExecutor == null || AnnotationConstants.NULL.equals(asyncExecutor)) {
|
||||
setAsyncExecutor(null);
|
||||
}
|
||||
else if (StringUtils.hasText(asyncExecutor)) {
|
||||
setAsyncExecutor(beanFactory.getBean(asyncExecutor, Executor.class));
|
||||
}
|
||||
}
|
||||
|
||||
super.onInit();
|
||||
@@ -114,7 +129,7 @@ public class AnnotationGatewayProxyFactoryBean extends GatewayProxyFactoryBean {
|
||||
"'defaultHeaders' are not allowed when a 'mapper' is provided");
|
||||
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfHasText(mapper,
|
||||
.acceptIfCondition(hasMapper && getMapper() == null, mapper,
|
||||
value -> setMapper(beanFactory.getBean(value, MethodArgsMessageMapper.class)));
|
||||
|
||||
if (hasDefaultHeaders || hasDefaultPayloadExpression) {
|
||||
@@ -124,23 +139,26 @@ public class AnnotationGatewayProxyFactoryBean extends GatewayProxyFactoryBean {
|
||||
gatewayMethodMetadata.setPayloadExpression(EXPRESSION_PARSER.parseExpression(defaultPayloadExpression));
|
||||
}
|
||||
|
||||
Map<String, Expression> headerExpressions = Arrays.stream(defaultHeaders)
|
||||
.collect(Collectors.toMap(
|
||||
header -> beanFactory.resolveEmbeddedValue((String) header.get("name")),
|
||||
header -> {
|
||||
String headerValue = beanFactory.resolveEmbeddedValue((String) header.get("value"));
|
||||
boolean hasValue = StringUtils.hasText(headerValue);
|
||||
Map<String, Expression> headerExpressions =
|
||||
Arrays.stream(defaultHeaders)
|
||||
.collect(Collectors.toMap(
|
||||
header -> beanFactory.resolveEmbeddedValue((String) header.get("name")),
|
||||
header -> {
|
||||
String headerValue =
|
||||
beanFactory.resolveEmbeddedValue((String) header.get("value"));
|
||||
boolean hasValue = StringUtils.hasText(headerValue);
|
||||
|
||||
String headerExpression =
|
||||
beanFactory.resolveEmbeddedValue((String) header.get("expression"));
|
||||
String headerExpression =
|
||||
beanFactory.resolveEmbeddedValue((String) header.get("expression"));
|
||||
|
||||
Assert.state(!(hasValue == StringUtils.hasText(headerExpression)),
|
||||
"exactly one of 'value' or 'expression' is required on a gateway's header.");
|
||||
Assert.state(!(hasValue == StringUtils.hasText(headerExpression)),
|
||||
"exactly one of 'value' or 'expression' is required on a gateway's " +
|
||||
"header.");
|
||||
|
||||
return hasValue ?
|
||||
new LiteralExpression(headerValue) :
|
||||
EXPRESSION_PARSER.parseExpression(headerExpression);
|
||||
}));
|
||||
return hasValue ?
|
||||
new LiteralExpression(headerValue) :
|
||||
EXPRESSION_PARSER.parseExpression(headerExpression);
|
||||
}));
|
||||
|
||||
gatewayMethodMetadata.setHeaderExpressions(headerExpressions);
|
||||
|
||||
|
||||
@@ -137,6 +137,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
|
||||
private AsyncTaskExecutor asyncExecutor = new SimpleAsyncTaskExecutor();
|
||||
|
||||
private boolean asyncExecutorExplicitlySet;
|
||||
|
||||
private Class<?> asyncSubmitType;
|
||||
|
||||
private Class<?> asyncSubmitListenableType;
|
||||
@@ -198,6 +200,16 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
this.defaultRequestChannelName = defaultRequestChannelName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MessageChannel getDefaultRequestChannel() {
|
||||
return this.defaultRequestChannel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String getDefaultRequestChannelName() {
|
||||
return this.defaultRequestChannelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default reply channel. If no default reply channel is provided,
|
||||
* and no reply channel is configured with annotations, an anonymous,
|
||||
@@ -221,6 +233,16 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
this.defaultReplyChannelName = defaultReplyChannelName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MessageChannel getDefaultReplyChannel() {
|
||||
return this.defaultReplyChannel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String getDefaultReplyChannelName() {
|
||||
return this.defaultReplyChannelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the error channel. If no error channel is provided, this gateway will
|
||||
* propagate Exceptions to the caller. To completely suppress Exceptions, provide
|
||||
@@ -242,6 +264,16 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
this.errorChannelName = errorChannelName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MessageChannel getErrorChannel() {
|
||||
return this.errorChannel;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String getErrorChannelName() {
|
||||
return this.errorChannelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default timeout value for sending request messages. If not explicitly
|
||||
* configured with an annotation, or on a method element, this value will be used.
|
||||
@@ -275,6 +307,11 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Expression getDefaultRequestTimeout() {
|
||||
return this.defaultRequestTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default timeout value for receiving reply messages. If not explicitly
|
||||
* configured with an annotation, or on a method element, this value will be used.
|
||||
@@ -308,6 +345,11 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Expression getDefaultReplyTimeout() {
|
||||
return this.defaultReplyTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShouldTrack(boolean shouldTrack) {
|
||||
this.shouldTrack = shouldTrack;
|
||||
@@ -326,12 +368,15 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
* @param executor The executor.
|
||||
*/
|
||||
public void setAsyncExecutor(@Nullable Executor executor) {
|
||||
if (executor == null && logger.isInfoEnabled()) {
|
||||
if (executor == null) {
|
||||
logger.info("A null executor disables the async gateway; " +
|
||||
"methods returning Future<?> will run on the calling thread");
|
||||
}
|
||||
this.asyncExecutor = (executor instanceof AsyncTaskExecutor || executor == null) ? (AsyncTaskExecutor) executor
|
||||
: new TaskExecutorAdapter(executor);
|
||||
this.asyncExecutor =
|
||||
(executor instanceof AsyncTaskExecutor || executor == null)
|
||||
? (AsyncTaskExecutor) executor
|
||||
: new TaskExecutorAdapter(executor);
|
||||
this.asyncExecutorExplicitlySet = true;
|
||||
}
|
||||
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
@@ -347,6 +392,11 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
this.globalMethodMetadata = globalMethodMetadata;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected GatewayMethodMetadata getGlobalMethodMetadata() {
|
||||
return this.globalMethodMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
@@ -361,10 +411,20 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
|
||||
this.argsMapper = mapper;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MethodArgsMessageMapper getMapper() {
|
||||
return this.argsMapper;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected AsyncTaskExecutor getAsyncExecutor() {
|
||||
return this.asyncExecutor;
|
||||
}
|
||||
|
||||
protected boolean isAsyncExecutorExplicitlySet() {
|
||||
return this.asyncExecutorExplicitlySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Map of {@link Method} to {@link MessagingGatewaySupport}
|
||||
* generated by this factory bean.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019 the original author or authors.
|
||||
* Copyright 2019-2020 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.
|
||||
@@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -32,11 +33,15 @@ import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.integration.MessageRejectedException;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.MessageChannels;
|
||||
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
|
||||
import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.integration.gateway.MethodArgsHolder;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
@@ -102,13 +107,26 @@ public class GatewayDslTests {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private Function<Object, Message<?>> functionGateay;
|
||||
private Function<Object, Message<?>> functionGateway;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("&functionGateway.gateway")
|
||||
private GatewayProxyFactoryBean functionGatewayFactoryBean;
|
||||
|
||||
@Test
|
||||
void testHeadersFromFunctionGateway() {
|
||||
Message<?> message = this.functionGateay.apply("testPayload");
|
||||
Message<?> message = this.functionGateway.apply("testPayload");
|
||||
assertThat(message.getPayload()).isEqualTo("testPayload");
|
||||
assertThat(message.getHeaders()).containsKeys("gatewayMethod", "gatewayArgs");
|
||||
|
||||
Map<Method, MessagingGatewaySupport> gateways = this.functionGatewayFactoryBean.getGateways();
|
||||
|
||||
MessagingGatewaySupport methodGateway = gateways.values().iterator().next();
|
||||
MessagingTemplate messagingTemplate =
|
||||
TestUtils.getPropertyValue(methodGateway, "messagingTemplate", MessagingTemplate.class);
|
||||
|
||||
assertThat(messagingTemplate.getReceiveTimeout()).isEqualTo(10);
|
||||
assertThat(messagingTemplate.getSendTimeout()).isEqualTo(20);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@@ -164,7 +182,9 @@ public class GatewayDslTests {
|
||||
return IntegrationFlows.from(MessageFunction.class,
|
||||
(gateway) -> gateway
|
||||
.header("gatewayMethod", MethodArgsHolder::getMethod)
|
||||
.header("gatewayArgs", MethodArgsHolder::getArgs))
|
||||
.header("gatewayArgs", MethodArgsHolder::getArgs)
|
||||
.replyTimeout(10)
|
||||
.requestTimeout(20))
|
||||
.bridge()
|
||||
.get();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user