From e50209d04541cd623e1d1df5b1d76ffda77debcc Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 20 Oct 2021 15:09:29 -0400 Subject: [PATCH] GH-3648: Fix @Gateway.payloadExpression Resolves https://github.com/spring-projects/spring-integration/issues/3648 When configuring a gateway proxy with XML, but specifying the payload expression on the method `@Gateway` annotation, the expression was ignored, even though it had been parsed. `@Payload` worked. With this change, if both `@Payload` and `@Gateway` are defined on a gateway method, `@Gateway.payloadExpression` wins. * Fix doc links. # Conflicts: # src/reference/asciidoc/whats-new.adoc --- .../gateway/GatewayProxyFactoryBean.java | 18 ++++++----------- .../config/xml/GatewayParserTests.java | 20 +++++++++++++++++++ .../integration/gateway/TestService.java | 9 ++++++++- src/reference/asciidoc/gateway.adoc | 20 +++++++++++++++++++ 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java index 792bb30d87..290e8a0ebf 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java @@ -111,6 +111,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint private final Class serviceInterface; + private final Set hasPayloadExpression = new HashSet<>(); + private MessageChannel defaultRequestChannel; private String defaultRequestChannelName; @@ -586,18 +588,7 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint } private boolean findPayloadExpression(Method method) { - boolean hasPayloadExpression = method.isAnnotationPresent(Payload.class); - if (!hasPayloadExpression) { - // check for the method metadata next - if (this.methodMetadataMap != null) { - GatewayMethodMetadata metadata = this.methodMetadataMap.get(method.getName()); - hasPayloadExpression = (metadata != null) && metadata.getPayloadExpression() != null; - } - else if (this.globalMethodMetadata != null) { - hasPayloadExpression = this.globalMethodMetadata.getPayloadExpression() != null; - } - } - return hasPayloadExpression; + return method.isAnnotationPresent(Payload.class) || this.hasPayloadExpression.contains(method); } @Nullable @@ -683,6 +674,9 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint } Expression payloadExpression = extractPayloadExpressionFromAnnotationOrMetadata(gatewayAnnotation, methodMetadata); + if (payloadExpression != null) { + this.hasPayloadExpression.add(method); + } String requestChannelName = extractRequestChannelFromAnnotationOrMetadata(gatewayAnnotation, methodMetadata); String replyChannelName = extractReplyChannelFromAnnotationOrMetadata(gatewayAnnotation, methodMetadata); Expression requestTimeout = extractRequestTimeoutFromAnnotationOrMetadata(gatewayAnnotation, methodMetadata); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/GatewayParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/GatewayParserTests.java index e472227709..0692d2104c 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/GatewayParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/GatewayParserTests.java @@ -148,6 +148,26 @@ public class GatewayParserTests { assertThat(result).isEqualTo("foo"); } + @Test + public void testRequestReplyNoArgsGw() { + PollableChannel requestChannel = (PollableChannel) context.getBean("requestChannel"); + MessageChannel replyChannel = (MessageChannel) context.getBean("replyChannel"); + this.startResponder(requestChannel, replyChannel); + TestService service = (TestService) context.getBean("requestReply"); + String result = service.noArgWithGateway(); + assertThat(result).isEqualTo("fromGwExpression"); + } + + @Test + public void testRequestReplyNoArgsBothAnn() { + PollableChannel requestChannel = (PollableChannel) context.getBean("requestChannel"); + MessageChannel replyChannel = (MessageChannel) context.getBean("replyChannel"); + this.startResponder(requestChannel, replyChannel); + TestService service = (TestService) context.getBean("requestReply"); + String result = service.noArgWithGatewayAndPayload(); + assertThat(result).isEqualTo("fromGwExpression"); + } + @Test public void testAsyncGateway() throws Exception { PollableChannel requestChannel = (PollableChannel) context.getBean("requestChannel"); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/TestService.java b/spring-integration-core/src/test/java/org/springframework/integration/gateway/TestService.java index 880f31bbb8..1f788ce6d1 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/TestService.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/TestService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -71,6 +71,13 @@ public interface TestService { throw new UnsupportedOperationException(); } + @Gateway(payloadExpression = "'fromGwExpression'", requestChannel = "requestChannel") + String noArgWithGateway(); + + @Payload("'fromPayloadAnnExpression'") + @Gateway(payloadExpression = "'fromGwExpression'", requestChannel = "requestChannel") + String noArgWithGatewayAndPayload(); + class MyCompletableFuture extends CompletableFuture { } diff --git a/src/reference/asciidoc/gateway.adoc b/src/reference/asciidoc/gateway.adoc index ac65faffbb..6bbacc7433 100644 --- a/src/reference/asciidoc/gateway.adoc +++ b/src/reference/asciidoc/gateway.adoc @@ -152,6 +152,10 @@ The following example shows how to add a different message header for each of tw In the preceding example a different value is set for the 'RESPONSE_TYPE' header, based on the gateway's method. +IMPORTANT: If you specify, for example, the `requestChannel` in `` as well as in a `@Gateway` annotation, the annotation value wins. + +NOTE: If a no-argument gateway is specified in XML, and the interface method has both a `@Payload` and `@Gateway` annotation (with a `payloadExpression` or a `payload-expression` in an `` element), the `@Payload` value is ignored. + ===== Expressions and "`Global`" Headers The `
` element supports `expression` as an alternative to `value`. @@ -349,6 +353,22 @@ public interface Cafe { } ---- +You can also use the `@Gateway` annotation. + +[source,xml] +---- +public interface Cafe { + + @Gateway(payloadExpression = "new java.util.Date()") + List retrieveOpenOrders(); + +} +---- + +NOTE: If both annotations are present (and the `payloadExpression` is provided), `@Gateway` wins. + +Also see <>. + If a method has no argument and no return value but does contain a payload expression, it is treated as a send-only operation. [[gateway-calling-default-methods]]