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 887ccfed35
commit 50b1116e5f
4 changed files with 67 additions and 25 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 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.
@@ -114,29 +114,35 @@ public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar,
: GatewayProxyFactoryBean.class);
if (hasDefaultHeaders || hasDefaultPayloadExpression) {
BeanDefinitionBuilder methodMetadataBuilder = BeanDefinitionBuilder.genericBeanDefinition(GatewayMethodMetadata.class);
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.
@@ -454,10 +454,15 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint
boolean hasPayloadExpression =
method.isAnnotationPresent(org.springframework.integration.annotation.Payload.class)
|| 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) {
if (shouldReply) {

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"/>
@@ -35,9 +35,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-2016 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.
@@ -68,6 +68,7 @@ import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.configuration.EnableIntegrationTests.TestMessagingGateway;
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;
@@ -441,6 +442,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 {
@@ -469,6 +490,11 @@ public class GatewayInterfaceTests {
void baz(String payload);
}
public interface NoArgumentsGateway {
String pullData();
}
public static class BazMapper implements MethodArgsMessageMapper {
@Override