INT-880 Added SpEL support to <header-enricher/> by including an "expression" attribute on the <header/> sub-element (to be used in place of either "value" or "ref").
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<String, Object> headersToAdd;
|
||||
|
||||
@@ -48,13 +59,60 @@ public class HeaderEnricher extends AbstractHeaderTransformer {
|
||||
this.overwrite = overwrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void transformHeaders(Map<String, Object> headers) {
|
||||
for (Map.Entry<String, Object> 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<String, Object> headerMap = new HashMap<String, Object>(message.getHeaders());
|
||||
for (Map.Entry<String, Object> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -901,7 +901,7 @@
|
||||
</xsd:element>
|
||||
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:choice>
|
||||
<xsd:attributeGroup ref="headerEnricherAttributes" />
|
||||
<xsd:attribute name="overwrite" type="xsd:string" />
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
@@ -946,7 +946,14 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<!-- TODO: xsd:attribute name="expression" type="xsd:string" / -->
|
||||
<xsd:attribute name="expression" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Expression to be evaulated at runtime to determine the header value.
|
||||
The EvaluationContext will include variables for 'payload' and 'headers'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
@@ -961,16 +968,6 @@
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:attributeGroup name="headerEnricherAttributes">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Provides the names of the standard configurable
|
||||
MessageHeaders.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="overwrite" type="xsd:string" />
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:element name="thread-pool-task-executor">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
|
||||
@@ -62,4 +62,12 @@
|
||||
<priority value="HIGH"/>
|
||||
</header-enricher>
|
||||
|
||||
<header-enricher input-channel="payloadExpressionInput">
|
||||
<header name="testHeader" expression="payload.name + 'bar'"/>
|
||||
</header-enricher>
|
||||
|
||||
<header-enricher input-channel="headerExpressionInput">
|
||||
<header name="testHeader2" expression="headers.testHeader1 + 'bar'"/>
|
||||
</header-enricher>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user