Skip to content

Commit

Permalink
GH-3648: Fix @Gateway.payloadExpression
Browse files Browse the repository at this point in the history
Resolves #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.
  • Loading branch information
garyrussell committed Oct 20, 2021
1 parent 035581b commit e84bab6
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint

private final Class<?> serviceInterface;

private final Set<Method> hasPayloadExpression = new HashSet<>();

private MessageChannel defaultRequestChannel;

private String defaultRequestChannelName;
Expand Down Expand Up @@ -599,18 +601,7 @@ private Object response(Class<?> returnType, boolean shouldReturnMessage, @Nulla
}

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
Expand Down Expand Up @@ -696,6 +687,9 @@ private MethodInvocationGateway createGatewayForMethod(Method method) {
}
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,26 @@ public void testRequestReply() {
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");
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -71,6 +71,13 @@ default void defaultMethodGateway(Object payload) {
throw new UnsupportedOperationException();
}

@Gateway(payloadExpression = "'fromGwExpression'", requestChannel = "requestChannel")
String noArgWithGateway();

@Payload("'fromPayloadAnnExpression'")
@Gateway(payloadExpression = "'fromGwExpression'", requestChannel = "requestChannel")
String noArgWithGatewayAndPayload();

class MyCompletableFuture extends CompletableFuture<String> {

}
Expand Down
20 changes: 20 additions & 0 deletions src/reference/asciidoc/gateway.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,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 `<int:method/>` 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 `<int:method/>` element), the `@Payload` value is ignored.

===== Expressions and "`Global`" Headers

The `<header/>` element supports `expression` as an alternative to `value`.
Expand Down Expand Up @@ -352,6 +356,22 @@ public interface Cafe {
}
----

You can also use the `@Gateway` annotation.

[source,xml]
----
public interface Cafe {
@Gateway(payloadExpression = "new java.util.Date()")
List<Order> retrieveOpenOrders();
}
----

NOTE: If both annotations are present (and the `payloadExpression` is provided), `@Gateway` wins.

Also see <<gateway-configuration-annotations>>.

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]]
Expand Down
7 changes: 7 additions & 0 deletions src/reference/asciidoc/whats-new.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,10 @@ See <<./web-sockets.adoc#web-sockets,WebSockets Support>> for more information.
The `JpaOutboundGateway` now supports an `Iterable` message payload for a `PersistMode.DELETE`.

See <<./jpa.adoc#jpa-outbound-channel-adapter,Outbound Channel Adapter>> for more information.

[[x55-gw]]
==== Gateway Changes

Previously, when using XML configuration, `@Gateway.payloadExpression` was ignored for no-argument methods.
There is one possible breaking change - if the method is annotated with `@Payload` as well as `@Gateway` (with a different expression) previously, the `@Payload` would be applied, now the `@Gateway.payloadExpression` is applied.
See <<./gateway.adoc#gateway-configuration-annotations,Gateway Configuration with Annotations and XML>> and <<./gateway.adoc#gateway-calling-no-argument-methods,Invoking No-Argument Methods>> for more information.

0 comments on commit e84bab6

Please sign in to comment.