Skip to content

Commit 71fd60f

Browse files
artembilangaryrussell
authored andcommitted
Add default expressions for ExpEvalReqHAdvice (#2738)
* Add default expressions for ExpEvalReqHAdvice https://stackoverflow.com/questions/54546728/how-to-handle-ftpoutboundadapter-connection-exception-in-spring-integration-flow When channels are configured for the `ExpressionEvaluatingRequestHandlerAdvice`, but no expressions, the logic is not performed. In other words: we can evaluate expressions, when no channels, but we don't send messages to channels when no expressions. * Provide default expressions to be evaluated to `payload`, when only channels are provided. * * Remove asserts for expression setters
1 parent e2d177e commit 71fd60f

File tree

4 files changed

+80
-50
lines changed

4 files changed

+80
-50
lines changed

spring-integration-core/src/main/java/org/springframework/integration/handler/advice/ExpressionEvaluatingRequestHandlerAdvice.java

Lines changed: 78 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 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.
@@ -16,17 +16,21 @@
1616

1717
package org.springframework.integration.handler.advice;
1818

19+
import org.springframework.beans.factory.BeanFactory;
1920
import org.springframework.expression.EvaluationContext;
2021
import org.springframework.expression.Expression;
21-
import org.springframework.expression.spel.standard.SpelExpressionParser;
2222
import org.springframework.expression.spel.support.StandardEvaluationContext;
2323
import org.springframework.integration.core.MessagingTemplate;
2424
import org.springframework.integration.expression.ExpressionUtils;
25+
import org.springframework.integration.expression.FunctionExpression;
2526
import org.springframework.integration.message.AdviceMessage;
27+
import org.springframework.lang.Nullable;
2628
import org.springframework.messaging.Message;
2729
import org.springframework.messaging.MessageChannel;
2830
import org.springframework.messaging.MessagingException;
31+
import org.springframework.messaging.core.DestinationResolver;
2932
import org.springframework.messaging.support.ErrorMessage;
33+
import org.springframework.util.StringUtils;
3034

3135
/**
3236
* Used to advise {@link org.springframework.messaging.MessageHandler}s.
@@ -36,53 +40,61 @@
3640
* containing the evaluation result in its payload and the {@code inputMessage} property containing
3741
* the original message that was sent to the endpoint.
3842
* The failure expression is NOT evaluated if the success expression throws an exception.
43+
* <p>
44+
* When expressions are not configured, but channels are, the default expression is evaluated
45+
* just into a {@code payload} from the message.
3946
*
4047
* @author Gary Russell
4148
* @author Artem Bilan
49+
*
4250
* @since 2.2
4351
*
4452
*/
4553
public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHandlerAdvice {
4654

47-
private volatile Expression onSuccessExpression;
55+
private static final Expression DEFAULT_EXPRESSION = new FunctionExpression<Message<?>>(Message::getPayload);
56+
57+
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
4858

49-
private volatile MessageChannel successChannel;
59+
private Expression onSuccessExpression;
5060

51-
private volatile String successChannelName;
61+
private MessageChannel successChannel;
5262

53-
private volatile Expression onFailureExpression;
63+
private String successChannelName;
5464

55-
private volatile MessageChannel failureChannel;
65+
private Expression onFailureExpression;
5666

57-
private volatile String failureChannelName;
67+
private MessageChannel failureChannel;
5868

59-
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
69+
private String failureChannelName;
6070

61-
private volatile boolean trapException = false;
71+
private boolean trapException = false;
6272

63-
private volatile boolean returnFailureExpressionResult = false;
73+
private boolean returnFailureExpressionResult = false;
6474

65-
private volatile boolean propagateOnSuccessEvaluationFailures;
75+
private boolean propagateOnSuccessEvaluationFailures;
6676

67-
private volatile EvaluationContext evaluationContext;
77+
private EvaluationContext evaluationContext;
6878

6979
/**
7080
* Set the expression to evaluate against the message after a successful
7181
* handler invocation.
82+
* Defaults to {@code payload}, if {@code successChannel} is configured.
7283
* @param onSuccessExpression the SpEL expression.
7384
* @since 4.3.7
7485
*/
7586
public void setOnSuccessExpressionString(String onSuccessExpression) {
76-
this.onSuccessExpression = new SpelExpressionParser().parseExpression(onSuccessExpression);
87+
setOnSuccessExpression(EXPRESSION_PARSER.parseExpression(onSuccessExpression));
7788
}
7889

7990
/**
8091
* Set the expression to evaluate against the message after a successful
8192
* handler invocation.
93+
* Defaults to {@code payload}, if {@code successChannel} is configured.
8294
* @param onSuccessExpression the SpEL expression.
8395
* @since 5.0
8496
*/
85-
public void setOnSuccessExpression(Expression onSuccessExpression) {
97+
public void setOnSuccessExpression(@Nullable Expression onSuccessExpression) {
8698
this.onSuccessExpression = onSuccessExpression;
8799
}
88100

@@ -94,26 +106,28 @@ public void setOnSuccessExpression(Expression onSuccessExpression) {
94106
*/
95107
@Deprecated
96108
public void setExpressionOnSuccess(Expression onSuccessExpression) {
97-
this.onSuccessExpression = onSuccessExpression;
109+
setOnSuccessExpression(onSuccessExpression);
98110
}
99111

100112
/**
101113
* Set the expression to evaluate against the root message after a failed
102-
* handler invocation. The exception is available as the variable {@code #exception}
114+
* handler invocation. The exception is available as the variable {@code #exception}.
115+
* Defaults to {@code payload}, if {@code failureChannel} is configured.
103116
* @param onFailureExpression the SpEL expression.
104117
* @since 4.3.7
105118
*/
106119
public void setOnFailureExpressionString(String onFailureExpression) {
107-
this.onFailureExpression = new SpelExpressionParser().parseExpression(onFailureExpression);
120+
setOnFailureExpression(EXPRESSION_PARSER.parseExpression(onFailureExpression));
108121
}
109122

110123
/**
111124
* Set the expression to evaluate against the root message after a failed
112-
* handler invocation. The exception is available as the variable {@code #exception}
125+
* handler invocation. The exception is available as the variable {@code #exception}.
126+
* Defaults to {@code payload}, if {@code failureChannel} is configured.
113127
* @param onFailureExpression the SpEL expression.
114128
* @since 5.0
115129
*/
116-
public void setOnFailureExpression(Expression onFailureExpression) {
130+
public void setOnFailureExpression(@Nullable Expression onFailureExpression) {
117131
this.onFailureExpression = onFailureExpression;
118132
}
119133

@@ -125,7 +139,7 @@ public void setOnFailureExpression(Expression onFailureExpression) {
125139
*/
126140
@Deprecated
127141
public void setExpressionOnFailure(Expression onFailureExpression) {
128-
this.onFailureExpression = onFailureExpression;
142+
setOnFailureExpression(onFailureExpression);
129143
}
130144

131145
/**
@@ -178,7 +192,6 @@ public void setTrapException(boolean trapException) {
178192
/**
179193
* If true, the result of evaluating the onFailureExpression will
180194
* be returned as the result of AbstractReplyProducingMessageHandler.handleRequestMessage(Message).
181-
*
182195
* @param returnFailureExpressionResult true to return the result of the evaluation.
183196
*/
184197
public void setReturnFailureExpressionResult(boolean returnFailureExpressionResult) {
@@ -200,24 +213,39 @@ public void setPropagateEvaluationFailures(boolean propagateOnSuccessEvaluationF
200213
@Override
201214
protected void onInit() {
202215
super.onInit();
203-
if (this.getBeanFactory() != null) {
204-
this.messagingTemplate.setBeanFactory(this.getBeanFactory());
216+
BeanFactory beanFactory = getBeanFactory();
217+
if (beanFactory != null) {
218+
this.messagingTemplate.setBeanFactory(beanFactory);
219+
}
220+
221+
if (this.onSuccessExpression == null
222+
&& (this.successChannel != null || StringUtils.hasText(this.successChannelName))) {
223+
224+
this.onSuccessExpression = DEFAULT_EXPRESSION;
225+
}
226+
227+
if (this.onFailureExpression == null
228+
&& (this.failureChannel != null || StringUtils.hasText(this.failureChannelName))) {
229+
230+
this.onFailureExpression = DEFAULT_EXPRESSION;
205231
}
206232
}
207233

208234
@Override
209-
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
235+
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message)
236+
throws Exception { // NOSONAR
237+
210238
try {
211239
Object result = callback.execute();
212240
if (this.onSuccessExpression != null) {
213-
this.evaluateSuccessExpression(message);
241+
evaluateSuccessExpression(message);
214242
}
215243
return result;
216244
}
217245
catch (Exception e) {
218-
Exception actualException = this.unwrapExceptionIfNecessary(e);
246+
Exception actualException = unwrapExceptionIfNecessary(e);
219247
if (this.onFailureExpression != null) {
220-
Object evalResult = this.evaluateFailureExpression(message, actualException);
248+
Object evalResult = evaluateFailureExpression(message, actualException);
221249
if (this.returnFailureExpressionResult) {
222250
return evalResult;
223251
}
@@ -229,68 +257,69 @@ protected Object doInvoke(ExecutionCallback callback, Object target, Message<?>
229257
}
230258
}
231259

232-
private void evaluateSuccessExpression(Message<?> message) throws Exception {
260+
private void evaluateSuccessExpression(Message<?> message) throws Exception { // NOSONAR
233261
Object evalResult;
234-
boolean evaluationFailed = false;
235262
try {
236-
evalResult = this.onSuccessExpression.getValue(this.prepareEvaluationContextToUse(null), message);
263+
evalResult = this.onSuccessExpression.getValue(prepareEvaluationContextToUse(null), message);
237264
}
238265
catch (Exception e) {
239266
evalResult = e;
240-
evaluationFailed = true;
241267
}
242-
if (this.successChannel == null && this.successChannelName != null && getChannelResolver() != null) {
243-
this.successChannel = getChannelResolver().resolveDestination(this.successChannelName);
268+
DestinationResolver<MessageChannel> channelResolver = getChannelResolver();
269+
if (this.successChannel == null && this.successChannelName != null && channelResolver != null) {
270+
this.successChannel = channelResolver.resolveDestination(this.successChannelName);
244271
}
245272
if (evalResult != null && this.successChannel != null) {
246-
AdviceMessage<?> resultMessage = new AdviceMessage<Object>(evalResult, message);
273+
AdviceMessage<?> resultMessage = new AdviceMessage<>(evalResult, message);
247274
this.messagingTemplate.send(this.successChannel, resultMessage);
248275
}
249-
if (evaluationFailed && this.propagateOnSuccessEvaluationFailures) {
276+
if (evalResult instanceof Exception && this.propagateOnSuccessEvaluationFailures) {
250277
throw (Exception) evalResult;
251278
}
252279
}
253280

254-
private Object evaluateFailureExpression(Message<?> message, Exception exception) throws Exception {
281+
private Object evaluateFailureExpression(Message<?> message, Exception exception) {
255282
Object evalResult;
256283
try {
257-
evalResult = this.onFailureExpression.getValue(this.prepareEvaluationContextToUse(exception), message);
284+
evalResult = this.onFailureExpression.getValue(prepareEvaluationContextToUse(exception), message);
258285
}
259286
catch (Exception e) {
260287
evalResult = e;
261288
logger.error("Failure expression evaluation failed for " + message + ": " + e.getMessage());
262289
}
263-
if (this.failureChannel == null && this.failureChannelName != null && getChannelResolver() != null) {
264-
this.failureChannel = getChannelResolver().resolveDestination(this.failureChannelName);
290+
DestinationResolver<MessageChannel> channelResolver = getChannelResolver();
291+
if (this.failureChannel == null && this.failureChannelName != null && channelResolver != null) {
292+
this.failureChannel = channelResolver.resolveDestination(this.failureChannelName);
265293
}
266294
if (evalResult != null && this.failureChannel != null) {
267-
MessagingException messagingException = new MessageHandlingExpressionEvaluatingAdviceException(message,
268-
"Handler Failed", this.unwrapThrowableIfNecessary(exception), evalResult);
269-
ErrorMessage resultMessage = new ErrorMessage(messagingException);
270-
this.messagingTemplate.send(this.failureChannel, resultMessage);
295+
MessagingException messagingException =
296+
new MessageHandlingExpressionEvaluatingAdviceException(message, "Handler Failed",
297+
unwrapThrowableIfNecessary(exception), evalResult);
298+
ErrorMessage errorMessage = new ErrorMessage(messagingException);
299+
this.messagingTemplate.send(this.failureChannel, errorMessage);
271300
}
272301
return evalResult;
273302
}
274303

275304
protected StandardEvaluationContext createEvaluationContext() {
276-
return ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
305+
return ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
277306
}
278307

279308
/**
280309
* If we don't need variables (i.e., exception is null)
281310
* we can use a singleton context; otherwise we need a new one each time.
282-
* @param exception
311+
* @param exception the {@link Exception} to use in the context.
283312
* @return The context.
284313
*/
285314
private EvaluationContext prepareEvaluationContextToUse(Exception exception) {
286315
EvaluationContext evaluationContextToUse;
287316
if (exception != null) {
288-
evaluationContextToUse = this.createEvaluationContext();
317+
evaluationContextToUse = createEvaluationContext();
289318
evaluationContextToUse.setVariable("exception", exception);
290319
}
291320
else {
292321
if (this.evaluationContext == null) {
293-
this.evaluationContext = this.createEvaluationContext();
322+
this.evaluationContext = createEvaluationContext();
294323
}
295324
evaluationContextToUse = this.evaluationContext;
296325
}
@@ -305,6 +334,7 @@ public static class MessageHandlingExpressionEvaluatingAdviceException extends M
305334

306335
public MessageHandlingExpressionEvaluatingAdviceException(Message<?> message, String description,
307336
Throwable cause, Object evaluationResult) {
337+
308338
super(message, description, cause);
309339
this.evaluationResult = evaluationResult;
310340
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,6 @@ public MessageHandler myHandler() {
11631163
@Bean
11641164
public Advice myHandlerAdvice() {
11651165
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
1166-
advice.setOnSuccessExpressionString("payload");
11671166
advice.setSuccessChannel(myHandlerSuccessChannel());
11681167
return advice;
11691168
}

spring-integration-core/src/test/java/org/springframework/integration/dsl/flows/IntegrationFlowTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,6 @@ public static class ContextConfiguration2 {
644644
@Bean
645645
public Advice expressionAdvice() {
646646
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
647-
advice.setOnSuccessExpressionString("payload");
648647
advice.setSuccessChannel(this.successChannel);
649648
return advice;
650649
}

src/reference/asciidoc/handler-advice.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ An additional property, called `inputMessage`, contains the original message sen
406406
A message sent to the `failureChannel` (when the handler throws an exception) is an `ErrorMessage` with a payload of `MessageHandlingExpressionEvaluatingAdviceException`.
407407
Like all `MessagingException` instances, this payload has `failedMessage` and `cause` properties, as well as an additional property called `evaluationResult`, which contains the result of the expression evaluation.
408408

409+
NOTE: Starting with version 5.1.3, if channels are configured, but expressions are not provided, the default expression is used to evaluate to the `payload` of the message.
410+
409411
When an exception is thrown in the scope of the advice, by default, that exception is thrown to the caller after any `failureExpression` is evaluated.
410412
If you wish to suppress throwing the exception, set the `trapException` property to `true`.
411413
The following advice shows how to configure an advice with Java DSL:

0 commit comments

Comments
 (0)