Skip to content

Commit

Permalink
INT-2763 Send AdviceMessage from Advice
Browse files Browse the repository at this point in the history
The ExpressionEvaluatingMessageHandlerAdvice now sends an
AdviceMessage which has a payload of the original message, and
a property 'inputMessage' referencing the message that was
sent to the advised handler.

INT-2763 Expression Advice Reference Doc Update

Update to reflect the new behavior.
  • Loading branch information
garyrussell authored and olegz committed Sep 21, 2012
1 parent c8da053 commit 2340e6f
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.Message;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;

Expand All @@ -35,7 +36,8 @@
* @since 2.2
*
*/
public abstract class AbstractRequestHandlerAdvice implements MethodInterceptor {
public abstract class AbstractRequestHandlerAdvice extends IntegrationObjectSupport
implements MethodInterceptor {

protected final Log logger = LogFactory.getLog(this.getClass());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,21 @@
*/
package org.springframework.integration.handler.advice;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.StringUtils;
import org.springframework.integration.message.AdviceMessage;
import org.springframework.integration.message.ErrorMessage;
import org.springframework.integration.util.ExpressionUtils;
import org.springframework.util.Assert;

/**
* Used to advise {@link MessageHandler}s.
Expand All @@ -40,62 +42,44 @@
* @since 2.2
*
*/
public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHandlerAdvice
implements BeanFactoryAware {
public class ExpressionEvaluatingRequestHandlerAdvice extends AbstractRequestHandlerAdvice {

private final ExpressionEvaluatingMessageProcessor<Object> onSuccessMessageProcessor;
private volatile Expression onSuccessExpression;

private final MessageChannel successChannel;
private volatile MessageChannel successChannel;

private final ExpressionEvaluatingMessageProcessor<Object> onFailureMessageProcessor;
private volatile Expression onFailureExpression;

private final MessageChannel failureChannel;
private volatile MessageChannel failureChannel;

private final MessagingTemplate messagingTemplate = new MessagingTemplate();

private volatile boolean trapException = false;

private volatile boolean returnFailureExpressionResult = false;

private volatile BeanFactory beanFactory;

private volatile boolean propagateOnSuccessEvaluationFailures;

/**
* @param onSuccessExpression
* @param successChannel
* @param onFailureExpression
* @param failureChannel
*/
public ExpressionEvaluatingRequestHandlerAdvice(Expression onSuccessExpression, MessageChannel successChannel,
Expression onFailureExpression, MessageChannel failureChannel) {
if (onSuccessExpression != null) {
this.onSuccessMessageProcessor = new ExpressionEvaluatingMessageProcessor<Object>(onSuccessExpression);
this.onSuccessMessageProcessor.setBeanFactory(this.beanFactory);
}
else {
this.onSuccessMessageProcessor = null;
}
this.successChannel = successChannel;
if (onFailureExpression != null) {
this.onFailureMessageProcessor = new ExpressionEvaluatingMessageProcessor<Object>(onFailureExpression);
onFailureMessageProcessor.setBeanFactory(this.beanFactory);
}
else {
this.onFailureMessageProcessor = null;
}
this.failureChannel = failureChannel;
private volatile EvaluationContext evaluationContext;

public void setOnSuccessExpression(String onSuccessExpression) {
Assert.notNull(onSuccessExpression, "'onSuccessExpression' must not be null");
this.onSuccessExpression = new SpelExpressionParser().parseExpression(onSuccessExpression);
}

public ExpressionEvaluatingRequestHandlerAdvice(String onSuccessExpression, MessageChannel successChannel,
String onFailureExpression, MessageChannel failureChannel) {
this(!StringUtils.hasText(onSuccessExpression) ? null :
new SpelExpressionParser().parseExpression(onSuccessExpression),
successChannel,
!StringUtils.hasText(onFailureExpression) ? null :
new SpelExpressionParser().parseExpression(onFailureExpression),
failureChannel);
public void setOnFailureExpression(String onFailureExpression) {
Assert.notNull(onFailureExpression, "'onFailureExpression' must not be null");
this.onFailureExpression = new SpelExpressionParser().parseExpression(onFailureExpression);
}

public void setSuccessChannel(MessageChannel successChannel) {
Assert.notNull(successChannel,"'successChannel' must not be null");
this.successChannel = successChannel;
}

public void setFailureChannel(MessageChannel failureChannel) {
Assert.notNull(failureChannel,"'failureChannel' must not be null");
this.failureChannel = failureChannel;
}

/**
Expand Down Expand Up @@ -126,22 +110,18 @@ public void setPropagateEvaluationFailures(boolean propagateOnSuccessEvaluationF
this.propagateOnSuccessEvaluationFailures = propagateOnSuccessEvaluationFailures;
}

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}

@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
try {
Object result = callback.execute();
if (this.onSuccessMessageProcessor != null) {
evaluateExpression(message, this.onSuccessMessageProcessor, this.successChannel, this.propagateOnSuccessEvaluationFailures);
if (this.onSuccessExpression != null) {
this.evaluateSuccessExpression(message);
}
return result;
}
catch (Exception e) {
if (this.onFailureMessageProcessor != null) {
Object evalResult = evaluateExpression(message, this.onFailureMessageProcessor, this.failureChannel, false);
if (this.onFailureExpression != null) {
Object evalResult = this.evaluateFailureExpression(message, e);
if (this.returnFailureExpressionResult) {
return evalResult;
}
Expand All @@ -153,28 +133,90 @@ protected Object doInvoke(ExecutionCallback callback, Object target, Message<?>
}
}

private Object evaluateExpression(Message<?> message,
ExpressionEvaluatingMessageProcessor<Object> expressionEvaluatingMessageProcessor,
MessageChannel resultChannel, boolean propagateEvaluationFailure) throws Exception {
private void evaluateSuccessExpression(Message<?> message) throws Exception {
Object evalResult;
boolean evaluationFailed = false;
try {
evalResult = expressionEvaluatingMessageProcessor.processMessage(message);
evalResult = this.onSuccessExpression.getValue(this.prepareEvaluationContextToUse(null), message);
}
catch (Exception e) {
evalResult = e;
evaluationFailed = true;
}
if (evalResult != null && resultChannel != null) {
message = MessageBuilder.fromMessage(message)
.setHeader(MessageHeaders.POSTPROCESS_RESULT, evalResult)
.build();
this.messagingTemplate.send(resultChannel, message);
if (evalResult != null && this.successChannel != null) {
AdviceMessage resultMessage = new AdviceMessage(evalResult, message);
this.messagingTemplate.send(this.successChannel, resultMessage);
}
if (evaluationFailed && propagateEvaluationFailure) {
if (evaluationFailed && this.propagateOnSuccessEvaluationFailures) {
throw (Exception) evalResult;
}
}

private Object evaluateFailureExpression(Message<?> message, Exception exception) throws Exception {
Object evalResult;
try {
evalResult = this.onFailureExpression.getValue(this.prepareEvaluationContextToUse(exception), message);
}
catch (Exception e) {
evalResult = e;
logger.error("Failure expression evaluation failed for " + message + ": " + e.getMessage());
}
if (evalResult != null && this.failureChannel != null) {
MessagingException messagingException = new MessageHandlingExpressionEvaluatingAdviceException(message,
"Handler Failed", exception, evalResult);
ErrorMessage resultMessage = new ErrorMessage(messagingException);
this.messagingTemplate.send(this.failureChannel, resultMessage);
}
return evalResult;
}

protected StandardEvaluationContext createEvaluationContext(){
if (this.getBeanFactory() != null) {
return ExpressionUtils.createStandardEvaluationContext(new BeanFactoryResolver(this.getBeanFactory()),
this.getConversionService());
}
else {
return ExpressionUtils.createStandardEvaluationContext(this.getConversionService());
}
}

/**
* If we don't need variables (i.e., exception is null)
* we can use a singleton context; otherwise we need a new one each time.
* @param exception
* @return The context.
*/
private EvaluationContext prepareEvaluationContextToUse(Exception exception) {
EvaluationContext evaluationContextToUse;
if (exception != null) {
evaluationContextToUse = this.createEvaluationContext();
evaluationContextToUse.setVariable("exception", exception);
}
else {
if (this.evaluationContext == null) {
this.evaluationContext = this.createEvaluationContext();
}
evaluationContextToUse = this.evaluationContext;
}
return evaluationContextToUse;
}

public static class MessageHandlingExpressionEvaluatingAdviceException extends MessagingException {

private static final long serialVersionUID = 1L;

private final Object evaluationResult;

public MessageHandlingExpressionEvaluatingAdviceException(Message<?> message, String description,
Throwable cause, Object evaluationResult) {
super(message, description, cause);
this.evaluationResult = evaluationResult;
}

public Object getEvaluationResult() {
return evaluationResult;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2002-2012 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.message;

import java.util.Map;

import org.springframework.integration.Message;

/**
* A message implementation that is produced by an advice after
* successful message handling.
* Contains the result of the expression evaluation in the payload
* and the original message that the advice passed to the
* handler.
* .
* @author Gary Russell
* @since 2.2
*/
public class AdviceMessage extends GenericMessage<Object> {

private static final long serialVersionUID = 1L;

private final Message<?> inputMessage;

public AdviceMessage(Object payload, Message<?> inputMessage) {
super(payload);
this.inputMessage = inputMessage;
}

public AdviceMessage(Object payload, Map<String, Object> headers, Message<?> inputMessage) {
super(payload, headers);
this.inputMessage = inputMessage;
}

public Message<?> getInputMessage() {
return inputMessage;
}

}
Loading

0 comments on commit 2340e6f

Please sign in to comment.