From 63b657b52a81ecbd245c2c12eb2d3ebc03860a25 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 25 Nov 2009 00:01:31 +0000 Subject: [PATCH] INT-880 Added SpEL support to by including an "expression" attribute on the
sub-element (to be used in place of either "value" or "ref"). --- .../xml/HeaderEnricherParserSupport.java | 10 ++- .../transformer/HeaderEnricher.java | 72 +++++++++++++++++-- .../config/xml/spring-integration-2.0.xsd | 21 +++--- .../xml/HeaderEnricherTests-context.xml | 8 +++ .../config/xml/HeaderEnricherTests.java | 34 +++++++++ 5 files changed, 124 insertions(+), 21 deletions(-) diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java index f4ec193c93..d84acdfaa4 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.transformer.HeaderEnricher.ExpressionHolder; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -105,17 +106,22 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar if (headerName != null) { String value = headerElement.getAttribute("value"); String ref = headerElement.getAttribute("ref"); + String expression = headerElement.getAttribute("expression"); boolean isValue = StringUtils.hasText(value); boolean isRef = StringUtils.hasText(ref); - if (!(isValue ^ isRef)) { + boolean isExpression = StringUtils.hasText(expression); + if (!(isValue ^ (isRef ^ isExpression))) { parserContext.getReaderContext().error( - "Exactly one of the 'value' or 'ref' attributes is required.", element); + "Exactly one of the 'ref', 'value', or 'expression' attributes is required.", element); } if (isValue) { Object headerValue = (headerType != null) ? new TypedStringValue(value, headerType) : value; headers.put(headerName, headerValue); } + else if (isExpression) { + headers.put(headerName, new ExpressionHolder(expression, headerType)); + } else { headers.put(headerName, new RuntimeBeanReference(ref)); } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java index de36f5107e..9e4fd233d9 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java @@ -16,8 +16,19 @@ package org.springframework.integration.transformer; +import java.util.HashMap; import java.util.Map; +import org.springframework.context.expression.MapAccessor; +import org.springframework.expression.EvaluationException; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParseException; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessagingException; +import org.springframework.integration.message.MessageBuilder; import org.springframework.util.Assert; /** @@ -28,7 +39,7 @@ import org.springframework.util.Assert; * * @author Mark Fisher */ -public class HeaderEnricher extends AbstractHeaderTransformer { +public class HeaderEnricher implements Transformer { private final Map headersToAdd; @@ -48,13 +59,60 @@ public class HeaderEnricher extends AbstractHeaderTransformer { this.overwrite = overwrite; } - @Override - protected final void transformHeaders(Map headers) { - for (Map.Entry entry : this.headersToAdd.entrySet()) { - String key = entry.getKey(); - if (this.overwrite || headers.get(key) == null) { - headers.put(key, entry.getValue()); + public Message transform(Message message) { + try { + Map headerMap = new HashMap(message.getHeaders()); + for (Map.Entry entry : this.headersToAdd.entrySet()) { + String key = entry.getKey(); + if (this.overwrite || headerMap.get(key) == null) { + Object value = entry.getValue(); + if (value instanceof ExpressionHolder) { + value = ((ExpressionHolder) value).evaluate(message); + } + headerMap.put(key, value); + } } + return MessageBuilder.withPayload(message.getPayload()).copyHeaders(headerMap).build(); + } + catch (Exception e) { + throw new MessagingException(message, "failed to transform message headers", e); + } + } + + + public static class ExpressionHolder { + + private static final ExpressionParser parser = new SpelExpressionParser(); + + + private final String expressionString; + + private final Class expectedType; + + private volatile Expression parsedExpression; + + + /** + * Create a holder object for the given expression String and the expected type + * of the expression evaluation result. The expectedType may be null if unknown. + */ + public ExpressionHolder(String expressionString, Class expectedType) { + this.expressionString = expressionString; + this.expectedType = expectedType; + } + + + private Object evaluate(Message message) throws ParseException, EvaluationException { + if (this.parsedExpression == null) { + synchronized (this) { + this.parsedExpression = parser.parseExpression(this.expressionString); + } + } + StandardEvaluationContext context = new StandardEvaluationContext(message); + context.addPropertyAccessor(new MapAccessor()); + return (this.expectedType != null) + ? this.parsedExpression.getValue(context, this.expectedType) + : this.parsedExpression.getValue(context); } } diff --git a/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd b/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd index d70975e696..991fc709bd 100644 --- a/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd +++ b/org.springframework.integration/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.0.xsd @@ -901,7 +901,7 @@ - + @@ -946,7 +946,14 @@ - + + + + Expression to be evaulated at runtime to determine the header value. + The EvaluationContext will include variables for 'payload' and 'headers'. + + + @@ -961,16 +968,6 @@ - - - - Provides the names of the standard configurable - MessageHeaders. - - - - - diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml index b50cdeb571..0be000d44b 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml @@ -62,4 +62,12 @@ + +
+ + + +
+ + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java index e7ac1a5f40..e5633bed5f 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java @@ -29,6 +29,7 @@ import org.springframework.integration.core.Message; import org.springframework.integration.core.MessageChannel; import org.springframework.integration.core.MessagePriority; import org.springframework.integration.gateway.SimpleMessagingGateway; +import org.springframework.integration.message.MessageBuilder; import org.springframework.integration.message.StringMessage; import org.springframework.integration.transformer.MessageTransformationException; import org.springframework.test.context.ContextConfiguration; @@ -115,4 +116,37 @@ public class HeaderEnricherTests { assertEquals(MessagePriority.HIGH, result.getHeaders().getPriority()); } + @Test + public void expressionUsingPayload() { + SimpleMessagingGateway gateway = new SimpleMessagingGateway(); + gateway.setRequestChannel(context.getBean("payloadExpressionInput", MessageChannel.class)); + Message result = gateway.sendAndReceiveMessage(new TestBean("foo")); + assertNotNull(result); + assertEquals("foobar", result.getHeaders().get("testHeader")); + } + + @Test + public void expressionUsingHeader() { + SimpleMessagingGateway gateway = new SimpleMessagingGateway(); + gateway.setRequestChannel(context.getBean("headerExpressionInput", MessageChannel.class)); + Message message = MessageBuilder.withPayload("test").setHeader("testHeader1", "foo").build(); + Message result = gateway.sendAndReceiveMessage(message); + assertNotNull(result); + assertEquals("foobar", result.getHeaders().get("testHeader2")); + } + + + public static class TestBean { + + private final String name; + + TestBean(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + } + }