INT-4448, INT-4449: Fix Gateway for no-arg method (#2420)

* INT-4448, INT-4449: Fix Gateway for no-arg method

JIRA: https://jira.spring.io/browse/INT-4448
JIRA: https://jira.spring.io/browse/INT-4449

When we are not interested in the `payload` to send, we use a gateway
method without any args, but in this case for send operation (or
send-and-receive) we should specify a default `payloadExpression`

The `MessagingGatewayRegistrar` fails with `NPE` if we don't have a
any global headers and have `defaultPayloadExpression`

Also in this case the `GatewayProxyFactoryBean` fails to send
and fallbacks to receive with the meaning "no args, not payloadExpression"

* Fix `MessagingGatewayRegistrar` to check `hasDefaultHeaders` before
processing them
* Fix `GatewayProxyFactoryBean` to consult `this.globalMethodMetadata`
if there is no `payloadExpression` for the method specific metadata

**Cherry-pick to 5.0.x and 4.3.x**

* Remove `oracle-java8-installer` since it looks like the resource
is not available anymore:

```
Location: http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk-8u161-linux-x64.tar.gz?AuthParam=1523990114_ee8c82cbe67bc87d192cb79d3b902d2f [following]
--2018-04-17 18:33:14--  http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk-8u161-linux-x64.tar.gz?AuthParam=1523990114_ee8c82cbe67bc87d192cb79d3b902d2f
Connecting to download.oracle.com (download.oracle.com)|23.53.120.105|:80... connected.
HTTP request sent, awaiting response... 404 Not Found
2018-04-17 18:35:15 ERROR 404: Not Found.
```
This commit is contained in:
Artem Bilan
2018-04-18 13:48:44 -04:00
committed by Gary Russell
parent 7d8fd16e0c
commit dc095322a2
5 changed files with 65 additions and 25 deletions

View File

@@ -11,7 +11,6 @@ addons:
- mongodb-3.0-precise
packages:
- mongodb-org-server
- oracle-java8-installer
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 the original author or authors.
* Copyright 2014-2018 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.
@@ -100,28 +100,33 @@ public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar
if (hasDefaultHeaders || hasDefaultPayloadExpression) {
BeanDefinitionBuilder methodMetadataBuilder =
BeanDefinitionBuilder.genericBeanDefinition(GatewayMethodMetadata.class);
if (hasDefaultPayloadExpression) {
methodMetadataBuilder.addPropertyValue("payloadExpression", defaultPayloadExpression);
}
Map<String, Object> headerExpressions = new ManagedMap<String, Object>();
for (Map<String, Object> header : defaultHeaders) {
String headerValue = (String) header.get("value");
String headerExpression = (String) header.get("expression");
boolean hasValue = StringUtils.hasText(headerValue);
if (hasValue == StringUtils.hasText(headerExpression)) {
throw new BeanDefinitionStoreException("exactly one of 'value' or 'expression' " +
"is required on a gateway's header.");
if (hasDefaultHeaders) {
Map<String, Object> headerExpressions = new ManagedMap<String, Object>();
for (Map<String, Object> header : defaultHeaders) {
String headerValue = (String) header.get("value");
String headerExpression = (String) header.get("expression");
boolean hasValue = StringUtils.hasText(headerValue);
if (hasValue == StringUtils.hasText(headerExpression)) {
throw new BeanDefinitionStoreException("exactly one of 'value' or 'expression' " +
"is required on a gateway's header.");
}
BeanDefinition expressionDef =
new RootBeanDefinition(hasValue ? LiteralExpression.class : ExpressionFactoryBean.class);
expressionDef.getConstructorArgumentValues()
.addGenericArgumentValue(hasValue ? headerValue : headerExpression);
headerExpressions.put((String) header.get("name"), expressionDef);
}
BeanDefinition expressionDef =
new RootBeanDefinition(hasValue ? LiteralExpression.class : ExpressionFactoryBean.class);
expressionDef.getConstructorArgumentValues()
.addGenericArgumentValue(hasValue ? headerValue : headerExpression);
headerExpressions.put((String) header.get("name"), expressionDef);
methodMetadataBuilder.addPropertyValue("headerExpressions", headerExpressions);
}
methodMetadataBuilder.addPropertyValue("headerExpressions", headerExpressions);
gatewayProxyBuilder.addPropertyValue("globalMethodMetadata", methodMetadataBuilder.getBeanDefinition());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -487,10 +487,15 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
int paramCount = method.getParameterTypes().length;
Object response = null;
boolean hasPayloadExpression = method.isAnnotationPresent(Payload.class);
if (!hasPayloadExpression && this.methodMetadataMap != null) {
if (!hasPayloadExpression) {
// check for the method metadata next
GatewayMethodMetadata metadata = this.methodMetadataMap.get(method.getName());
hasPayloadExpression = (metadata != null) && StringUtils.hasText(metadata.getPayloadExpression());
if (this.methodMetadataMap != null) {
GatewayMethodMetadata metadata = this.methodMetadataMap.get(method.getName());
hasPayloadExpression = (metadata != null) && StringUtils.hasText(metadata.getPayloadExpression());
}
else if (this.globalMethodMetadata != null) {
hasPayloadExpression = StringUtils.hasText(this.globalMethodMetadata.getPayloadExpression());
}
}
if (paramCount == 0 && !hasPayloadExpression) {
Long receiveTimeout = null;

View File

@@ -10,7 +10,7 @@
</int:channel>
<int:gateway id="sampleGateway"
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests.Bar"
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests$Bar"
default-request-channel="requestChannelBaz"
error-channel="errorChannel">
<int:default-header name="name" expression="#gatewayMethod.name"/>
@@ -34,9 +34,14 @@
<int:channel id="requestChannelBaz"/>
<int:gateway id="customMappedGateway"
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests.Baz"
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests$Baz"
default-request-channel="requestChannelBaz" mapper="mapper"/>
<bean id="mapper" class="org.springframework.integration.gateway.GatewayInterfaceTests$BazMapper"/>
<int:gateway id="sampleGateway2"
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests$NoArgumentsGateway"
default-request-channel="requestChannelBar"
default-payload-expression="'foo'"/>
</beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -75,6 +75,7 @@ import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
@@ -487,6 +488,26 @@ public class GatewayInterfaceTests {
((SubscribableChannel) this.errorChannel).unsubscribe(messageHandler);
}
@Test
public void testGatewayWithNoArgsMethod() {
ConfigurableApplicationContext ac =
new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass());
DirectChannel channel = ac.getBean("requestChannelBar", DirectChannel.class);
channel.subscribe(new AbstractReplyProducingMessageHandler() {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
assertEquals("foo", requestMessage.getPayload());
return "FOO";
}
});
NoArgumentsGateway noArgumentsGateway = ac.getBean(NoArgumentsGateway.class);
assertEquals("FOO", noArgumentsGateway.pullData());
ac.close();
}
public interface Foo {
@@ -520,6 +541,11 @@ public class GatewayInterfaceTests {
void baz(String payload);
}
public interface NoArgumentsGateway {
String pullData();
}
public static class BazMapper implements MethodArgsMessageMapper {
@Override