diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java index 5e13e63032..6948e7ee06 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessor.java @@ -16,7 +16,12 @@ package org.springframework.integration.handler; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.AccessException; +import org.springframework.expression.BeanResolver; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; @@ -31,7 +36,7 @@ import org.springframework.integration.core.Message; * @author Mark Fisher * @since 2.0 */ -public class ExpressionEvaluatingMessageProcessor extends AbstractMessageProcessor { +public class ExpressionEvaluatingMessageProcessor extends AbstractMessageProcessor implements BeanFactoryAware { private final ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); @@ -40,6 +45,9 @@ public class ExpressionEvaluatingMessageProcessor extends AbstractMessageProcess private volatile Class expectedType = null; + /** + * Create an {@link ExpressionEvaluatingMessageProcessor} for the given expression String. + */ public ExpressionEvaluatingMessageProcessor(String expression) { try { this.expression = parser.parseExpression(expression); @@ -51,10 +59,30 @@ public class ExpressionEvaluatingMessageProcessor extends AbstractMessageProcess } + /** + * Set the result type expected from evaluation of the expression. + */ public void setExpectedType(Class expectedType) { this.expectedType = expectedType; } + /** + * Specify a BeanFactory in order to enable resolution via @beanName in the expression. + */ + public void setBeanFactory(final BeanFactory beanFactory) { + if (beanFactory != null) { + this.getEvaluationContext().setBeanResolver(new BeanResolver() { + public Object resolve(EvaluationContext context, String beanName) throws AccessException { + return beanFactory.getBean(beanName); + } + }); + } + } + + /** + * Processes the Message by evaluating the expression with that Message as the + * root object. The expression evaluation result Object will be returned. + */ public Object processMessage(Message message) { return this.evaluateExpression(this.expression, message, this.expectedType); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java index 26e6d0fd1c..d24d4b3fbf 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/ExpressionEvaluatingMessageProcessorTests.java @@ -22,22 +22,28 @@ import org.junit.Rule; import org.junit.Test; import org.junit.internal.matchers.TypeSafeMatcher; import org.junit.rules.ExpectedException; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.support.StaticApplicationContext; import org.springframework.expression.EvaluationException; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.StringMessage; /** * @author Dave Syer + * @author Mark Fisher * @since 2.0 - * */ public class ExpressionEvaluatingMessageProcessorTests { private static final Log logger = LogFactory.getLog(ExpressionEvaluatingMessageProcessorTests.class); + @Rule public ExpectedException expected = ExpectedException.none(); + @Test public void testProcessMessage() { ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload"); @@ -45,12 +51,19 @@ public class ExpressionEvaluatingMessageProcessorTests { } @Test - public void testProcessMessageWithDollar() { + public void testProcessMessageWithDollarInBrackets() { ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers['$id']"); StringMessage message = new StringMessage("foo"); assertEquals(message.getHeaders().getId(), processor.processMessage(message)); } + @Test + public void testProcessMessageWithDollarPropertyAccess() { + ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers.$id"); + StringMessage message = new StringMessage("foo"); + assertEquals(message.getHeaders().getId(), processor.processMessage(message)); + } + @Test public void testProcessMessageWithStaticKey() { ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers[headers.ID]"); @@ -58,6 +71,30 @@ public class ExpressionEvaluatingMessageProcessorTests { assertEquals(message.getHeaders().getId(), processor.processMessage(message)); } + @Test + public void testProcessMessageWithBeanAsMethodArgument() { + StaticApplicationContext context = new StaticApplicationContext(); + BeanDefinition beanDefinition = new RootBeanDefinition(String.class); + beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("bar"); + context.registerBeanDefinition("testString", beanDefinition); + ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.concat(@testString)"); + processor.setBeanFactory(context); + StringMessage message = new StringMessage("foo"); + assertEquals("foobar", processor.processMessage(message)); + } + + @Test + public void testProcessMessageWithMethodCallOnBean() { + StaticApplicationContext context = new StaticApplicationContext(); + BeanDefinition beanDefinition = new RootBeanDefinition(String.class); + beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("bar"); + context.registerBeanDefinition("testString", beanDefinition); + ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("@testString.concat(payload)"); + processor.setBeanFactory(context); + StringMessage message = new StringMessage("foo"); + assertEquals("barfoo", processor.processMessage(message)); + } + @Test public void testProcessMessageBadExpression() { expected.expect(new TypeSafeMatcher(Exception.class) { @@ -90,8 +127,8 @@ public class ExpressionEvaluatingMessageProcessorTests { description.appendText("cause to be UnsupportedOperationException but was ").appendValue(cause); } }); - ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.error()"); - assertEquals("foo", processor.processMessage(new GenericMessage(this))); + ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.throwRuntimeException()"); + assertEquals("foo", processor.processMessage(new GenericMessage(new TestPayload()))); } @Test @@ -108,17 +145,23 @@ public class ExpressionEvaluatingMessageProcessorTests { description.appendText("cause to be CheckedException but was ").appendValue(cause); } }); - ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.check()"); - assertEquals("foo", processor.processMessage(new GenericMessage(this))); + ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.throwCheckedException()"); + assertEquals("foo", processor.processMessage(new GenericMessage(new TestPayload()))); } - public String error() { - throw new UnsupportedOperationException("Expected test exception"); + + @SuppressWarnings("unused") + private static class TestPayload { + + public String throwRuntimeException() { + throw new UnsupportedOperationException("Expected test exception"); + } + + public String throwCheckedException() throws Exception { + throw new CheckedException("Expected test exception"); + } } - public String check() throws Exception { - throw new CheckedException("Expected test exception"); - } @SuppressWarnings("serial") private static final class CheckedException extends Exception {