INT-4203: Expression RH Advice Improvements
JIRA: https://jira.spring.io/browse/INT-4203 Add channel names for easier use in DSL. Add documentation (boot) example. Normalize Expression Setters; add Javadocs Polishing - SPR-15091 Tiny code style polishing
This commit is contained in:
committed by
Artem Bilan
parent
e2dd2c52d2
commit
77fd8a4684
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -28,7 +28,6 @@ import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Used to advise {@link MessageHandler}s.
|
||||
@@ -50,10 +49,14 @@ public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHan
|
||||
|
||||
private volatile MessageChannel successChannel;
|
||||
|
||||
private volatile String successChannelName;
|
||||
|
||||
private volatile Expression onFailureExpression;
|
||||
|
||||
private volatile MessageChannel failureChannel;
|
||||
|
||||
private volatile String failureChannelName;
|
||||
|
||||
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
|
||||
private volatile boolean trapException = false;
|
||||
@@ -64,34 +67,106 @@ public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHan
|
||||
|
||||
private volatile EvaluationContext evaluationContext;
|
||||
|
||||
public void setOnSuccessExpression(String onSuccessExpression) {
|
||||
Assert.notNull(onSuccessExpression, "'onSuccessExpression' must not be null");
|
||||
/**
|
||||
* Set the expression to evaluate against the message after a successful
|
||||
* handler invocation.
|
||||
* @param onSuccessExpression the SpEL expression.
|
||||
* @since 4.3.7
|
||||
*/
|
||||
public void setOnSuccessExpressionString(String onSuccessExpression) {
|
||||
this.onSuccessExpression = new SpelExpressionParser().parseExpression(onSuccessExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expression to evaluate against the message after a successful
|
||||
* handler invocation.
|
||||
* @param onSuccessExpression the SpEL expression.
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setOnSuccessExpression(Expression onSuccessExpression) {
|
||||
this.onSuccessExpression = onSuccessExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expression to evaluate against the message after a successful
|
||||
* handler invocation.
|
||||
* @param onSuccessExpression the SpEL expression.
|
||||
* @deprecated in favor of {@link #setOnSuccessExpression(Expression)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExpressionOnSuccess(Expression onSuccessExpression) {
|
||||
this.onSuccessExpression = onSuccessExpression;
|
||||
}
|
||||
|
||||
public void setOnFailureExpression(String onFailureExpression) {
|
||||
Assert.notNull(onFailureExpression, "'onFailureExpression' must not be null");
|
||||
/**
|
||||
* Set the expression to evaluate against the root message after a failed
|
||||
* handler invocation. The exception is available as the variable {@code #exception}
|
||||
* @param onFailureExpression the SpEL expression.
|
||||
* @since 4.3.7
|
||||
*/
|
||||
public void setOnFailureExpressionString(String onFailureExpression) {
|
||||
this.onFailureExpression = new SpelExpressionParser().parseExpression(onFailureExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expression to evaluate against the root message after a failed
|
||||
* handler invocation. The exception is available as the variable {@code #exception}
|
||||
* @param onFailureExpression the SpEL expression.
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setOnFailureExpression(Expression onFailureExpression) {
|
||||
this.onFailureExpression = onFailureExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expression to evaluate against the root message after a failed
|
||||
* handler invocation. The exception is available as the variable {@code #exception}
|
||||
* @param onFailureExpression the SpEL expression.
|
||||
* @deprecated in favor of {@link #setOnFailureExpression(Expression)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setExpressionOnFailure(Expression onFailureExpression) {
|
||||
this.onFailureExpression = onFailureExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel to which to send the {@link AdviceMessage} after evaluating the
|
||||
* success expression.
|
||||
* @param successChannel the channel.
|
||||
*/
|
||||
public void setSuccessChannel(MessageChannel successChannel) {
|
||||
Assert.notNull(successChannel, "'successChannel' must not be null");
|
||||
this.successChannel = successChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel name to which to send the {@link AdviceMessage} after evaluating
|
||||
* the success expression.
|
||||
* @param successChannelName the channel name.
|
||||
* @since 4.3.7
|
||||
*/
|
||||
public void setSuccessChannelName(String successChannelName) {
|
||||
this.successChannelName = successChannelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel to which to send the {@link ErrorMessage} after evaluating the
|
||||
* failure expression.
|
||||
* @param failureChannel the channel.
|
||||
*/
|
||||
public void setFailureChannel(MessageChannel failureChannel) {
|
||||
Assert.notNull(failureChannel, "'failureChannel' must not be null");
|
||||
this.failureChannel = failureChannel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel name to which to send the {@link ErrorMessage} after evaluating the
|
||||
* failure expression.
|
||||
* @param failureChannelName the channel name.
|
||||
* @since 4.3.7
|
||||
*/
|
||||
public void setFailureChannelName(String failureChannelName) {
|
||||
this.failureChannelName = failureChannelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, any exception will be caught and null returned.
|
||||
* Default false.
|
||||
@@ -112,10 +187,12 @@ public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHan
|
||||
}
|
||||
|
||||
/**
|
||||
* If true and an onSuccess expression evaluation fails with an exception, the exception will be thrown to the
|
||||
* caller. If false, the exception is caught. Default false. Ignored for onFailure expression evaluation - the
|
||||
* original exception will be propagated (unless trapException is true).
|
||||
* @param propagateOnSuccessEvaluationFailures The propagateOnSuccessEvaluationFailures to set.
|
||||
* If true and an onSuccess expression evaluation fails with an exception, the
|
||||
* exception will be thrown to the caller. If false, the exception is caught. Default
|
||||
* false. Ignored for onFailure expression evaluation - the original exception will be
|
||||
* propagated (unless trapException is true).
|
||||
* @param propagateOnSuccessEvaluationFailures The
|
||||
* propagateOnSuccessEvaluationFailures to set.
|
||||
*/
|
||||
public void setPropagateEvaluationFailures(boolean propagateOnSuccessEvaluationFailures) {
|
||||
this.propagateOnSuccessEvaluationFailures = propagateOnSuccessEvaluationFailures;
|
||||
@@ -163,6 +240,9 @@ public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHan
|
||||
evalResult = e;
|
||||
evaluationFailed = true;
|
||||
}
|
||||
if (this.successChannel == null && this.successChannelName != null && getChannelResolver() != null) {
|
||||
this.successChannel = getChannelResolver().resolveDestination(this.successChannelName);
|
||||
}
|
||||
if (evalResult != null && this.successChannel != null) {
|
||||
AdviceMessage<?> resultMessage = new AdviceMessage<Object>(evalResult, message);
|
||||
this.messagingTemplate.send(this.successChannel, resultMessage);
|
||||
@@ -181,6 +261,9 @@ public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHan
|
||||
evalResult = e;
|
||||
logger.error("Failure expression evaluation failed for " + message + ": " + e.getMessage());
|
||||
}
|
||||
if (this.failureChannel == null && this.failureChannelName != null && getChannelResolver() != null) {
|
||||
this.failureChannel = getChannelResolver().resolveDestination(this.failureChannelName);
|
||||
}
|
||||
if (evalResult != null && this.failureChannel != null) {
|
||||
MessagingException messagingException = new MessageHandlingExpressionEvaluatingAdviceException(message,
|
||||
"Handler Failed", this.unwrapThrowableIfNecessary(exception), evalResult);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -53,4 +53,12 @@ public class AdviceMessage<T> extends GenericMessage<T> {
|
||||
return this.inputMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(super.toString());
|
||||
builder.setLength(builder.length() - 1);
|
||||
builder.append(", inputMessage=").append(this.inputMessage.toString()).append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ public class IntegrationFlowTests {
|
||||
@Bean
|
||||
public Advice expressionAdvice() {
|
||||
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
|
||||
advice.setOnSuccessExpression("payload");
|
||||
advice.setOnSuccessExpressionString("payload");
|
||||
advice.setSuccessChannel(this.successChannel);
|
||||
return advice;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -143,8 +143,8 @@ public class AdvisedMessageHandlerTests {
|
||||
advice.setBeanFactory(mock(BeanFactory.class));
|
||||
advice.setSuccessChannel(successChannel);
|
||||
advice.setFailureChannel(failureChannel);
|
||||
advice.setOnSuccessExpression("'foo'");
|
||||
advice.setOnFailureExpression("'bar:' + #exception.message");
|
||||
advice.setOnSuccessExpressionString("'foo'");
|
||||
advice.setOnFailureExpressionString("'bar:' + #exception.message");
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
@@ -237,8 +237,8 @@ public class AdvisedMessageHandlerTests {
|
||||
advice.setBeanFactory(mock(BeanFactory.class));
|
||||
advice.setSuccessChannel(successChannel);
|
||||
advice.setFailureChannel(failureChannel);
|
||||
advice.setOnSuccessExpression("1/0");
|
||||
advice.setOnFailureExpression("1/0");
|
||||
advice.setOnSuccessExpressionString("1/0");
|
||||
advice.setOnFailureExpressionString("1/0");
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
@@ -301,8 +301,8 @@ public class AdvisedMessageHandlerTests {
|
||||
advice.setBeanFactory(mock(BeanFactory.class));
|
||||
advice.setSuccessChannel(successChannel);
|
||||
advice.setFailureChannel(failureChannel);
|
||||
advice.setOnSuccessExpression("1/0");
|
||||
advice.setOnFailureExpression("1/0");
|
||||
advice.setOnSuccessExpressionString("1/0");
|
||||
advice.setOnFailureExpressionString("1/0");
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
@@ -722,7 +722,7 @@ public class AdvisedMessageHandlerTests {
|
||||
ExpressionEvaluatingRequestHandlerAdvice expressionAdvice = new ExpressionEvaluatingRequestHandlerAdvice();
|
||||
expressionAdvice.setBeanFactory(mock(BeanFactory.class));
|
||||
// MessagingException / RuntimeException
|
||||
expressionAdvice.setOnFailureExpression("#exception.cause.message");
|
||||
expressionAdvice.setOnFailureExpressionString("#exception.cause.message");
|
||||
expressionAdvice.setReturnFailureExpressionResult(true);
|
||||
final AtomicInteger outerCounter = new AtomicInteger();
|
||||
adviceChain.add(new AbstractRequestHandlerAdvice() {
|
||||
@@ -769,7 +769,7 @@ public class AdvisedMessageHandlerTests {
|
||||
|
||||
ExpressionEvaluatingRequestHandlerAdvice expressionAdvice = new ExpressionEvaluatingRequestHandlerAdvice();
|
||||
expressionAdvice.setBeanFactory(mock(BeanFactory.class));
|
||||
expressionAdvice.setOnFailureExpression("#exception.message");
|
||||
expressionAdvice.setOnFailureExpressionString("#exception.message");
|
||||
expressionAdvice.setFailureChannel(errors);
|
||||
|
||||
adviceChain.add(new RequestHandlerRetryAdvice());
|
||||
@@ -849,7 +849,7 @@ public class AdvisedMessageHandlerTests {
|
||||
|
||||
ExpressionEvaluatingRequestHandlerAdvice expressionAdvice = new ExpressionEvaluatingRequestHandlerAdvice();
|
||||
expressionAdvice.setBeanFactory(mock(BeanFactory.class));
|
||||
expressionAdvice.setOnFailureExpression("'foo'");
|
||||
expressionAdvice.setOnFailureExpressionString("'foo'");
|
||||
expressionAdvice.setFailureChannel(errors);
|
||||
|
||||
Throwable theThrowable = new Throwable("foo");
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.handler.advice;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.handler.GenericHandler;
|
||||
import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice.MessageHandlingExpressionEvaluatingAdviceException;
|
||||
import org.springframework.integration.message.AdviceMessage;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 5.0
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
public class ExpressionEvaluatingRequestHandlerAdviceTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("advised.input")
|
||||
private MessageChannel in;
|
||||
|
||||
@Autowired
|
||||
private EERHAConfig config;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
this.in.send(new GenericMessage<>("good"));
|
||||
this.in.send(new GenericMessage<>("junk"));
|
||||
assertThat(config.successful, instanceOf(AdviceMessage.class));
|
||||
assertThat(config.successful.getPayload(), equalTo("good was successful"));
|
||||
assertThat(config.failed, instanceOf(ErrorMessage.class));
|
||||
Object evaluationResult = ((MessageHandlingExpressionEvaluatingAdviceException) config.failed.getPayload())
|
||||
.getEvaluationResult();
|
||||
assertThat((String) evaluationResult, startsWith("junk was bad, with reason:"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class EERHAConfig {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow advised() {
|
||||
return f -> f.handle((GenericHandler<String>) (payload, headers) -> {
|
||||
if (payload.equals("good")) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("some failure");
|
||||
}
|
||||
}, c -> c.advice(expressionAdvice()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Advice expressionAdvice() {
|
||||
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
|
||||
advice.setSuccessChannelName("success.input");
|
||||
advice.setOnSuccessExpressionString("payload + ' was successful'");
|
||||
advice.setFailureChannelName("failure.input");
|
||||
advice.setOnFailureExpressionString(
|
||||
"payload + ' was bad, with reason: ' + #exception.cause.message");
|
||||
advice.setTrapException(true);
|
||||
return advice;
|
||||
}
|
||||
|
||||
private Message<?> successful;
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow success() {
|
||||
return f -> f
|
||||
.handle(m -> this.successful = m);
|
||||
}
|
||||
|
||||
private Message<?> failed;
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow failure() {
|
||||
return f -> f
|
||||
.handle(m -> this.failed = m);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user