Skip to content

Commit a30dc10

Browse files
artembilangaryrussell
authored andcommitted
Improve messaging gateway mapping
The `#args` and `#gatewayMethod` SpEL variables have been deprecated for a while * Remove their population and usage in favor of `MethodArgsHolder` `root` of the evaluation context This change optimize a gateway mapping logic the way that there is no need in evaluation context for every call: we can just reuse a global one * Some other `GatewayMethodInboundMessageMapper` code style refactoring * Fix effected test classes and their configs
1 parent a9f511c commit a30dc10

13 files changed

+105
-122
lines changed

spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayMethodInboundMessageMapper.java

+40-58
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@
3838
import org.springframework.expression.EvaluationContext;
3939
import org.springframework.expression.Expression;
4040
import org.springframework.expression.spel.standard.SpelExpressionParser;
41-
import org.springframework.expression.spel.support.StandardEvaluationContext;
4241
import org.springframework.integration.expression.ExpressionUtils;
4342
import org.springframework.integration.mapping.InboundMessageMapper;
4443
import org.springframework.integration.mapping.MessageMappingException;
@@ -107,9 +106,7 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
107106

108107
private Expression payloadExpression;
109108

110-
private EvaluationContext payloadExpressionEvaluationContext;
111-
112-
private BeanFactory beanFactory;
109+
private EvaluationContext evaluationContext;
113110

114111
@Nullable
115112
private Expression sendTimeoutExpression;
@@ -148,18 +145,14 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
148145
this.globalHeaderExpressions = globalHeaderExpressions;
149146
this.parameterList = getMethodParameterList(method);
150147
this.payloadExpression = parsePayloadExpression(method);
151-
if (messageBuilderFactory == null) {
152-
this.messageBuilderFactory = new DefaultMessageBuilderFactory();
153-
}
154-
else {
155-
this.messageBuilderFactory = messageBuilderFactory;
156-
}
157-
if (mapper == null) {
158-
this.argsMapper = new DefaultMethodArgsMessageMapper();
159-
}
160-
else {
161-
this.argsMapper = mapper;
162-
}
148+
this.messageBuilderFactory =
149+
messageBuilderFactory == null
150+
? new DefaultMessageBuilderFactory()
151+
: messageBuilderFactory;
152+
this.argsMapper =
153+
mapper == null
154+
? new DefaultMethodArgsMessageMapper()
155+
: mapper;
163156
}
164157

165158

@@ -169,8 +162,7 @@ public void setPayloadExpression(Expression expressionString) {
169162

170163
@Override
171164
public void setBeanFactory(BeanFactory beanFactory) {
172-
this.beanFactory = beanFactory;
173-
this.payloadExpressionEvaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory);
165+
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory);
174166
}
175167

176168
public void setSendTimeoutExpression(Expression sendTimeoutExpression) {
@@ -197,38 +189,31 @@ private Message<?> mapArgumentsToMessage(Object[] arguments, @Nullable Map<Strin
197189
try {
198190
return this.argsMapper.toMessage(new MethodArgsHolder(this.method, arguments), headers);
199191
}
200-
catch (MessagingException e) { // NOSONAR to avoid if..else
201-
throw e;
192+
catch (MessagingException ex) { // NOSONAR to avoid if..else
193+
throw ex;
202194
}
203-
catch (Exception e) {
204-
throw new MessageMappingException("Failed to map arguments: " + Arrays.toString(arguments), e);
195+
catch (Exception ex) {
196+
throw new MessageMappingException("Failed to map arguments: " + Arrays.toString(arguments), ex);
205197
}
206198
}
207199

208-
private Map<String, Object> evaluateHeaders(EvaluationContext methodInvocationEvaluationContext,
209-
MethodArgsHolder methodArgsHolder, Map<String, Expression> headerExpressions) {
200+
private Map<String, Object> evaluateHeaders(MethodArgsHolder methodArgsHolder,
201+
Map<String, Expression> headerExpressions) {
210202

211203
Map<String, Object> evaluatedHeaders = new HashMap<>();
212204
for (Map.Entry<String, Expression> entry : headerExpressions.entrySet()) {
213-
Object value = entry.getValue().getValue(methodInvocationEvaluationContext, methodArgsHolder);
205+
Object value = entry.getValue()
206+
.getValue(GatewayMethodInboundMessageMapper.this.evaluationContext, methodArgsHolder);
214207
evaluatedHeaders.put(entry.getKey(), value);
215208
}
216209
return evaluatedHeaders;
217210
}
218211

219-
// TODO Remove in the future release. The MethodArgsHolder as a root object covers this use-case.
220-
private StandardEvaluationContext createMethodInvocationEvaluationContext(Object[] arguments) {
221-
StandardEvaluationContext context = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
222-
context.setVariable("args", arguments);
223-
context.setVariable("gatewayMethod", this.method);
224-
return context;
225-
}
226-
227212
@Nullable
228213
private Object evaluatePayloadExpression(String expressionString, Object argumentValue) {
229214
Expression expression =
230215
this.parameterPayloadExpressions.computeIfAbsent(expressionString, PARSER::parseExpression);
231-
return expression.getValue(this.payloadExpressionEvaluationContext, argumentValue);
216+
return expression.getValue(this.evaluationContext, argumentValue);
232217
}
233218

234219

@@ -287,23 +272,19 @@ private static Expression parsePayloadExpression(Method method) {
287272

288273
public class DefaultMethodArgsMessageMapper implements MethodArgsMessageMapper {
289274

290-
private final MessageBuilderFactory msgBuilderFactory =
291-
GatewayMethodInboundMessageMapper.this.messageBuilderFactory;
292-
293275
@Override
294276
public Message<?> toMessage(MethodArgsHolder holder, @Nullable Map<String, Object> headersToMap) {
295277
Object messageOrPayload = null;
296278
boolean foundPayloadAnnotation = false;
297279
Object[] arguments = holder.getArgs();
298-
EvaluationContext methodInvocationEvaluationContext = createMethodInvocationEvaluationContext(arguments);
299280
Map<String, Object> headersToPopulate =
300281
headersToMap != null
301282
? new HashMap<>(headersToMap)
302283
: new HashMap<>();
303284
if (GatewayMethodInboundMessageMapper.this.payloadExpression != null) {
304285
messageOrPayload =
305286
GatewayMethodInboundMessageMapper.this.payloadExpression.getValue(
306-
methodInvocationEvaluationContext, holder);
287+
GatewayMethodInboundMessageMapper.this.evaluationContext, holder);
307288
}
308289
for (int i = 0; i < GatewayMethodInboundMessageMapper.this.parameterList.size(); i++) {
309290
Object argumentValue = arguments[i];
@@ -337,8 +318,8 @@ else if (GatewayMethodInboundMessageMapper.this.payloadExpression == null) {
337318
() -> "The 'payload' (or 'Message') for gateway [" + GatewayMethodInboundMessageMapper.this.method +
338319
"] method call cannot be determined (must not be 'null') from the provided arguments: " +
339320
Arrays.toString(arguments));
340-
populateSendAndReplyTimeoutHeaders(methodInvocationEvaluationContext, holder, headersToPopulate);
341-
return buildMessage(holder, headersToPopulate, messageOrPayload, methodInvocationEvaluationContext);
321+
populateSendAndReplyTimeoutHeaders(holder, headersToPopulate);
322+
return buildMessage(holder, headersToPopulate, messageOrPayload);
342323
}
343324

344325
private void headerOrHeaders(Map<String, Object> headersToPopulate, Object argumentValue,
@@ -406,45 +387,46 @@ private void processMapArgument(Object messageOrPayload, boolean foundPayloadAnn
406387
copyHeaders(argumentValue, headersToPopulate);
407388
}
408389

409-
private void populateSendAndReplyTimeoutHeaders(EvaluationContext methodInvocationEvaluationContext,
410-
MethodArgsHolder methodArgsHolder, Map<String, Object> headersToPopulate) {
390+
private void populateSendAndReplyTimeoutHeaders(MethodArgsHolder methodArgsHolder,
391+
Map<String, Object> headersToPopulate) {
411392

412393
if (GatewayMethodInboundMessageMapper.this.sendTimeoutExpression != null) {
413394
headersToPopulate.computeIfAbsent(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER,
414395
v -> GatewayMethodInboundMessageMapper.this.sendTimeoutExpression
415-
.getValue(methodInvocationEvaluationContext, methodArgsHolder, Long.class));
396+
.getValue(GatewayMethodInboundMessageMapper.this.evaluationContext,
397+
methodArgsHolder, Long.class));
416398
}
417399
if (GatewayMethodInboundMessageMapper.this.replyTimeoutExpression != null) {
418400
headersToPopulate.computeIfAbsent(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER,
419401
v -> GatewayMethodInboundMessageMapper.this.replyTimeoutExpression
420-
.getValue(methodInvocationEvaluationContext, methodArgsHolder, Long.class));
402+
.getValue(GatewayMethodInboundMessageMapper.this.evaluationContext,
403+
methodArgsHolder, Long.class));
421404
}
422405
}
423406

424407
private Message<?> buildMessage(MethodArgsHolder methodArgsHolder, Map<String, Object> headers,
425-
Object messageOrPayload, EvaluationContext methodInvocationEvaluationContext) {
408+
Object messageOrPayload) {
426409

410+
MessageBuilderFactory msgBuilderFactory = GatewayMethodInboundMessageMapper.this.messageBuilderFactory;
427411
AbstractIntegrationMessageBuilder<?> builder =
428-
(messageOrPayload instanceof Message)
429-
? this.msgBuilderFactory.fromMessage((Message<?>) messageOrPayload)
430-
: this.msgBuilderFactory.withPayload(messageOrPayload);
412+
(messageOrPayload instanceof Message<?> msg)
413+
? msgBuilderFactory.fromMessage(msg)
414+
: msgBuilderFactory.withPayload(messageOrPayload);
431415
builder.copyHeadersIfAbsent(headers);
432416
// Explicit headers in XML override any @Header annotations...
433417
if (!CollectionUtils.isEmpty(GatewayMethodInboundMessageMapper.this.headerExpressions)) {
434-
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext,
435-
methodArgsHolder, GatewayMethodInboundMessageMapper.this.headerExpressions);
418+
Map<String, Object> evaluatedHeaders =
419+
evaluateHeaders(methodArgsHolder, GatewayMethodInboundMessageMapper.this.headerExpressions);
436420
builder.copyHeaders(evaluatedHeaders);
437421
}
438422
// ...whereas global (default) headers do not...
439423
if (!CollectionUtils.isEmpty(GatewayMethodInboundMessageMapper.this.globalHeaderExpressions)) {
440-
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext,
441-
methodArgsHolder, GatewayMethodInboundMessageMapper.this.globalHeaderExpressions);
424+
Map<String, Object> evaluatedHeaders =
425+
evaluateHeaders(methodArgsHolder,
426+
GatewayMethodInboundMessageMapper.this.globalHeaderExpressions);
442427
builder.copyHeadersIfAbsent(evaluatedHeaders);
443428
}
444-
if (GatewayMethodInboundMessageMapper.this.headers != null) {
445-
builder.copyHeadersIfAbsent(GatewayMethodInboundMessageMapper.this.headers);
446-
}
447-
return builder.build();
429+
return builder.copyHeadersIfAbsent(GatewayMethodInboundMessageMapper.this.headers).build();
448430
}
449431

450432
}

spring-integration-core/src/test/java/org/springframework/integration/aggregator/BarrierMessageHandlerTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2020 the original author or authors.
2+
* Copyright 2015-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1467,7 +1467,7 @@ public interface ConditionalGateway {
14671467
@TestMessagingGateway
14681468
public interface TestGateway {
14691469

1470-
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "#gatewayMethod.name"))
1470+
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "method.name"))
14711471
String echo(String payload);
14721472

14731473
@Gateway(requestChannel = "sendAsyncChannel")
@@ -1482,7 +1482,7 @@ public interface TestGateway {
14821482
@TestMessagingGateway2
14831483
public interface TestGateway2 {
14841484

1485-
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "#gatewayMethod.name"))
1485+
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "method.name"))
14861486
String echo2(String payload);
14871487

14881488
}

spring-integration-core/src/test/java/org/springframework/integration/gateway/AsyncGatewayTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -393,10 +393,10 @@ private interface TestEchoService {
393393

394394
CompletableFuture<Message<?>> returnMessageListenable(String s);
395395

396-
@Gateway(headers = @GatewayHeader(name = "method", expression = "#gatewayMethod.name"))
396+
@Gateway(headers = @GatewayHeader(name = "method", expression = "method.name"))
397397
CustomFuture returnCustomFuture(String s);
398398

399-
@Gateway(headers = @GatewayHeader(name = "method", expression = "#gatewayMethod.name"))
399+
@Gateway(headers = @GatewayHeader(name = "method", expression = "method.name"))
400400
Future<?> returnCustomFutureWithTypeFuture(String s);
401401

402402
Mono<String> returnStringPromise(String s);

spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests-context.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests$Bar"
1414
default-request-channel="requestChannelBaz"
1515
error-channel="errorChannel">
16-
<int:default-header name="name" expression="#gatewayMethod.name"/>
17-
<int:default-header name="string" expression="#gatewayMethod.toString()"/>
18-
<int:default-header name="object" expression="#gatewayMethod"/>
16+
<int:default-header name="name" expression="method.name"/>
17+
<int:default-header name="string" expression="method.toString()"/>
18+
<int:default-header name="object" expression="method"/>
1919
<int:method name="baz">
2020
<int:header name="name" value="overrideGlobal"/>
2121
</int:method>

spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -522,8 +522,8 @@ public interface Foo {
522522

523523
void baz(String payload);
524524

525-
@Gateway(payloadExpression = "#args[0]", requestChannel = "lateReplyChannel",
526-
requestTimeoutExpression = "#args[1]", replyTimeoutExpression = "#args[2]")
525+
@Gateway(payloadExpression = "args[0]", requestChannel = "lateReplyChannel",
526+
requestTimeoutExpression = "args[1]", replyTimeoutExpression = "args[2]")
527527
String lateReply(String payload, long requestTimeout, long replyTimeout);
528528

529529
}
@@ -651,7 +651,7 @@ public GatewayProxyFactoryBean annotationGatewayProxyFactoryBean() {
651651
@Profile("gatewayTest")
652652
public interface Int2634Gateway {
653653

654-
@Gateway(requestChannel = "gatewayChannel", payloadExpression = "#args[0]")
654+
@Gateway(requestChannel = "gatewayChannel", payloadExpression = "args[0]")
655655
Object test1(Map<Object, ?> map);
656656

657657
@Gateway(requestChannel = "gatewayChannel")

spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests2-context.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<int:gateway id="sampleGateway"
99
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests.Bar"
1010
default-request-channel="requestChannelBaz" default-payload-expression="'foo'">
11-
<int:default-header name="name" expression="#gatewayMethod.name"/>
12-
<int:default-header name="string" expression="#gatewayMethod.toString()"/>
13-
<int:default-header name="object" expression="#gatewayMethod"/>
11+
<int:default-header name="name" expression="method.name"/>
12+
<int:default-header name="string" expression="method.toString()"/>
13+
<int:default-header name="object" expression="method"/>
1414
<int:method name="baz">
1515
<int:header name="name" value="overrideGlobal"/>
1616
</int:method>

0 commit comments

Comments
 (0)