diff --git a/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java b/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java index 079f51d644..2fb81b43c1 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/transformer/HeaderEnricher.java @@ -49,12 +49,14 @@ public class HeaderEnricher implements Transformer { private static final Log logger = LogFactory.getLog(HeaderEnricher.class); - private final Map headersToAdd; + private final Map headersToAdd; private volatile MessageProcessor messageProcessor; private volatile boolean defaultOverwrite = false; + private volatile boolean shouldSkipNulls = true; + public HeaderEnricher() { this(null); @@ -63,7 +65,7 @@ public class HeaderEnricher implements Transformer { /** * Create a HeaderEnricher with the given map of headers. */ - public HeaderEnricher(Map headersToAdd) { + public HeaderEnricher(Map headersToAdd) { this.headersToAdd = (headersToAdd != null) ? headersToAdd : new HashMap(); } @@ -76,19 +78,29 @@ public class HeaderEnricher implements Transformer { this.defaultOverwrite = defaultOverwrite; } + /** + * Specify whether null values, such as might be returned from an expression evaluation, + * should be skipped. The default value is true. Set this to false if a + * null value should trigger removal of the corresponding header instead. + */ + public void setShouldSkipNulls(boolean shouldSkipNulls) { + this.shouldSkipNulls = shouldSkipNulls; + } + public Message transform(Message message) { try { Map headerMap = new HashMap(message.getHeaders()); this.addHeadersFromMessageProcessor(message, headerMap); - for (Map.Entry entry : this.headersToAdd.entrySet()) { + for (Map.Entry entry : this.headersToAdd.entrySet()) { String key = entry.getKey(); ValueHolder valueHolder = entry.getValue(); Boolean shouldOverwrite = valueHolder.isOverwrite(); if (shouldOverwrite == null) { shouldOverwrite = this.defaultOverwrite; } - if (shouldOverwrite || headerMap.get(key) == null) { - headerMap.put(key, valueHolder.evaluate(message)); + Object value = valueHolder.evaluate(message); + if ((value != null && shouldOverwrite) || headerMap.get(key) == null || (value == null && !this.shouldSkipNulls)) { + headerMap.put(key, value); } } return MessageBuilder.withPayload(message.getPayload()).copyHeaders(headerMap).build(); diff --git a/spring-integration-xml/src/main/java/org/springframework/integration/xml/enricher/XPathHeaderEnricher.java b/spring-integration-xml/src/main/java/org/springframework/integration/xml/enricher/XPathHeaderEnricher.java index 511e04d529..f334e0b4d8 100644 --- a/spring-integration-xml/src/main/java/org/springframework/integration/xml/enricher/XPathHeaderEnricher.java +++ b/spring-integration-xml/src/main/java/org/springframework/integration/xml/enricher/XPathHeaderEnricher.java @@ -16,20 +16,18 @@ package org.springframework.integration.xml.enricher; -import java.util.Collections; import java.util.Map; -import java.util.Set; import org.w3c.dom.Node; import org.springframework.integration.core.Message; -import org.springframework.integration.message.MessageBuilder; -import org.springframework.integration.transformer.Transformer; +import org.springframework.integration.transformer.HeaderEnricher; import org.springframework.integration.xml.DefaultXmlPayloadConverter; import org.springframework.integration.xml.XmlPayloadConverter; import org.springframework.integration.xml.xpath.XPathEvaluationType; -import org.springframework.util.StringUtils; +import org.springframework.util.Assert; import org.springframework.xml.xpath.XPathExpression; +import org.springframework.xml.xpath.XPathExpressionFactory; /** * Transformer implementation that evaluates XPath expressions against the @@ -37,71 +35,60 @@ import org.springframework.xml.xpath.XPathExpression; * header. The header names will match the keys in the map of expressions. * * @author Jonas Partner + * @author Mark Fisher * @since 2.0 */ -public class XPathHeaderEnricher implements Transformer { - - private final Map expressionMap; - - private Map evaluationTypes; - - private XPathEvaluationType defaultEvaluationType = XPathEvaluationType.STRING_RESULT; - - private volatile boolean skipNullResults = true; - - private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter(); - +public class XPathHeaderEnricher extends HeaderEnricher { /** * Create an instance of XPathHeaderEnricher using a map with header names as keys - * and XPathExpressions to evaluate as the values. All XPathExpressions are evaluated - * as String results by default. - * - * @param expressionMap + * and XPathExpressionValueHolders to evaluate the values. */ - public XPathHeaderEnricher(Map expressionMap) { - this.expressionMap = Collections.unmodifiableMap(expressionMap); + public XPathHeaderEnricher(Map expressionMap) { + super(expressionMap); } - public void setConverter(XmlPayloadConverter converter) { - this.converter = converter; - } + public static class XPathExpressionValueHolder implements ValueHolder { - public void setSkipNullResults(boolean skipNullResults) { - this.skipNullResults = skipNullResults; - } + private final XPathExpression expression; - public void setEvaluationTypes(Map evaluationTypes) { - this.evaluationTypes = evaluationTypes; - } + private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter(); - public void setDefaultEvaluationType(XPathEvaluationType defaultEvaluationType) { - this.defaultEvaluationType = defaultEvaluationType; - } + private volatile XPathEvaluationType evaluationType = XPathEvaluationType.STRING_RESULT; - public final Message transform(Message message) { - MessageBuilder builder = MessageBuilder.fromMessage(message); - Node node = this.converter.convertToNode(message.getPayload()); - Set keys = this.expressionMap.keySet(); - for (String key : keys) { - XPathExpression expression = this.expressionMap.get(key); - XPathEvaluationType evalType = this.defaultEvaluationType; - if (this.evaluationTypes != null && this.evaluationTypes.containsKey(key)) { - evalType = this.evaluationTypes.get(key); - } - setHeader(node, key, expression, evalType, builder); + private volatile Boolean overwrite = null; + + + public XPathExpressionValueHolder(String expression) { + Assert.hasText(expression, "expression must have text"); + this.expression = XPathExpressionFactory.createXPathExpression(expression); } - return builder.build(); - } - private void setHeader(Node node, String headerName, XPathExpression expression, - XPathEvaluationType evaluationType, MessageBuilder builder) { - Object result = evaluationType.evaluateXPath(expression, node); - boolean nullOrEmptyString = (result == null) || - (result instanceof String && !StringUtils.hasLength((String) result)); - if (!nullOrEmptyString || !this.skipNullResults) { - builder.setHeader(headerName, result); + public XPathExpressionValueHolder(XPathExpression expression) { + Assert.notNull(expression, "expression must not be null"); + this.expression = expression; + } + + public void setEvaluationType(XPathEvaluationType evaluationType) { + this.evaluationType = evaluationType; + } + + public void setOverwrite(Boolean overwrite) { + this.overwrite = overwrite; + } + + public Object evaluate(Message message) { + Node node = converter.convertToNode(message.getPayload()); + Object result = this.evaluationType.evaluateXPath(this.expression, node); + if (result instanceof String && ((String) result).length() == 0) { + result = null; + } + return result; + } + + public Boolean isOverwrite() { + return this.overwrite; } } diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/enricher/XPathHeaderEnricherTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/enricher/XPathHeaderEnricherTests.java index cc72bd183c..1fbac683fa 100644 --- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/enricher/XPathHeaderEnricherTests.java +++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/enricher/XPathHeaderEnricherTests.java @@ -17,6 +17,7 @@ package org.springframework.integration.xml.enricher; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import java.util.HashMap; @@ -27,9 +28,8 @@ import org.junit.Test; import org.springframework.integration.core.Message; import org.springframework.integration.core.MessageHeaders; import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.xml.enricher.XPathHeaderEnricher.XPathExpressionValueHolder; import org.springframework.integration.xml.xpath.XPathEvaluationType; -import org.springframework.xml.xpath.XPathExpression; -import org.springframework.xml.xpath.XPathExpressionFactory; /** * @author Jonas Partner @@ -39,9 +39,9 @@ public class XPathHeaderEnricherTests { @Test public void simpleStringEvaluation() { - Map expressionMap = new HashMap(); - expressionMap.put("one", XPathExpressionFactory.createXPathExpression("/root/elementOne")); - expressionMap.put("two", XPathExpressionFactory.createXPathExpression("/root/elementTwo")); + Map expressionMap = new HashMap(); + expressionMap.put("one", new XPathExpressionValueHolder("/root/elementOne")); + expressionMap.put("two", new XPathExpressionValueHolder("/root/elementTwo")); String docAsString = "12"; XPathHeaderEnricher enricher = new XPathHeaderEnricher(expressionMap); Message result = enricher.transform(MessageBuilder.withPayload(docAsString).build()); @@ -52,8 +52,8 @@ public class XPathHeaderEnricherTests { @Test public void nullValuesSkippedByDefault() { - Map expressionMap = new HashMap(); - expressionMap.put("two", XPathExpressionFactory.createXPathExpression("/root/elementTwo")); + Map expressionMap = new HashMap(); + expressionMap.put("two", new XPathExpressionValueHolder("/root/elementTwo")); String docAsString = "1"; XPathHeaderEnricher enricher = new XPathHeaderEnricher(expressionMap); Message result = enricher.transform(MessageBuilder.withPayload(docAsString).build()); @@ -63,27 +63,29 @@ public class XPathHeaderEnricherTests { @Test public void notSkippingNullValues() { - Map expressionMap = new HashMap(); - expressionMap.put("two", XPathExpressionFactory.createXPathExpression("/root/elementTwo")); + Map expressionMap = new HashMap(); + expressionMap.put("two", new XPathExpressionValueHolder("/root/elementTwo")); String docAsString = "1"; XPathHeaderEnricher enricher = new XPathHeaderEnricher(expressionMap); - enricher.setSkipNullResults(false); - Message result = enricher.transform(MessageBuilder.withPayload(docAsString).build()); + enricher.setShouldSkipNulls(false); + Message result = enricher.transform(MessageBuilder.withPayload(docAsString).setHeader("two", "x").build()); MessageHeaders headers = result.getHeaders(); - assertEquals("no value set for two when result was null and ignore null was false", - "", headers.get("two")); + assertNull(headers.get("two")); + assertFalse(headers.containsKey("two")); } @Test public void numberEvaluationResult() { - Map expressionMap = new HashMap(); - expressionMap.put("one", XPathExpressionFactory.createXPathExpression("/root/elementOne")); - expressionMap.put("two", XPathExpressionFactory.createXPathExpression("/root/elementTwo")); + Map expressionMap = new HashMap(); + XPathExpressionValueHolder expression1 = new XPathExpressionValueHolder("/root/elementOne"); + XPathExpressionValueHolder expression2 = new XPathExpressionValueHolder("/root/elementTwo"); + expression2.setEvaluationType(XPathEvaluationType.NUMBER_RESULT); + expressionMap.put("one", expression1); + expressionMap.put("two", expression2); Map evalTypeMap = new HashMap(); evalTypeMap.put("two", XPathEvaluationType.NUMBER_RESULT); String docAsString = "12"; XPathHeaderEnricher enricher = new XPathHeaderEnricher(expressionMap); - enricher.setEvaluationTypes(evalTypeMap); Message result = enricher.transform(MessageBuilder.withPayload(docAsString).build()); MessageHeaders headers = result.getHeaders(); assertEquals("Wrong value for element one expression", "1", headers.get("one"));