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 7e3f8e95b3..89b31b224a 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 @@ -66,6 +66,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar this.processHeaders(element, headers, parserContext); builder.addConstructorArgValue(headers); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "default-overwrite"); + this.postProcessHeaderEnricher(builder, element, parserContext); } protected void processHeaders(Element element, ManagedMap headers, ParserContext parserContext) { @@ -168,4 +169,10 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar } } + /** + * Subclasses may override this method to provide any additional processing. + */ + protected void postProcessHeaderEnricher(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { + } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/StandardHeaderEnricherParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/StandardHeaderEnricherParser.java index b465360e10..ace9f9155d 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/StandardHeaderEnricherParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/StandardHeaderEnricherParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -16,8 +16,13 @@ package org.springframework.integration.config.xml; +import org.w3c.dom.Element; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.core.MessageHeaders; import org.springframework.integration.core.MessagePriority; +import org.springframework.util.StringUtils; /** * Parser for the <header-enricher> element within the core integration @@ -38,4 +43,23 @@ public class StandardHeaderEnricherParser extends HeaderEnricherParserSupport { this.addElementToHeaderMapping("priority", MessageHeaders.PRIORITY, MessagePriority.class); } + @Override + protected void postProcessHeaderEnricher(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { + String ref = element.getAttribute("ref"); + String method = element.getAttribute("method"); + if (StringUtils.hasText(ref) || StringUtils.hasText(method)) { + if (!StringUtils.hasText(ref) || !StringUtils.hasText(method)) { + parserContext.getReaderContext().error( + "If either 'ref' or 'method' is provided, then they are both required.", + parserContext.extractSource(element)); + return; + } + BeanDefinitionBuilder processorBuilder = BeanDefinitionBuilder.genericBeanDefinition( + IntegrationNamespaceUtils.BASE_PACKAGE + ".handler.MethodInvokingMessageProcessor"); + processorBuilder.addConstructorArgReference(ref); + processorBuilder.addConstructorArgValue(method); + builder.addPropertyValue("messageProcessor", processorBuilder.getBeanDefinition()); + } + } + } 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 9a590a31ea..079f51d644 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 @@ -19,6 +19,9 @@ package org.springframework.integration.transformer; import java.util.HashMap; import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.context.expression.MapAccessor; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; @@ -29,9 +32,9 @@ 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.handler.MessageProcessor; import org.springframework.integration.handler.MethodInvokingMessageProcessor; import org.springframework.integration.message.MessageBuilder; -import org.springframework.util.Assert; /** * A Transformer that adds statically configured header values to a Message. @@ -43,20 +46,32 @@ import org.springframework.util.Assert; */ public class HeaderEnricher implements Transformer { + private static final Log logger = LogFactory.getLog(HeaderEnricher.class); + + private final Map headersToAdd; + private volatile MessageProcessor messageProcessor; + private volatile boolean defaultOverwrite = false; + public HeaderEnricher() { + this(null); + } + /** * Create a HeaderEnricher with the given map of headers. */ public HeaderEnricher(Map headersToAdd) { - Assert.notNull(headersToAdd, "headersToAdd must not be null"); - this.headersToAdd = headersToAdd; + this.headersToAdd = (headersToAdd != null) ? headersToAdd : new HashMap(); } + public void setMessageProcessor(MessageProcessor messageProcessor) { + this.messageProcessor = messageProcessor; + } + public void setDefaultOverwrite(boolean defaultOverwrite) { this.defaultOverwrite = defaultOverwrite; } @@ -64,6 +79,7 @@ public class HeaderEnricher implements Transformer { public Message transform(Message message) { try { Map headerMap = new HashMap(message.getHeaders()); + this.addHeadersFromMessageProcessor(message, headerMap); for (Map.Entry entry : this.headersToAdd.entrySet()) { String key = entry.getKey(); ValueHolder valueHolder = entry.getValue(); @@ -82,6 +98,28 @@ public class HeaderEnricher implements Transformer { } } + @SuppressWarnings("unchecked") + private void addHeadersFromMessageProcessor(Message message, Map headerMap) { + if (this.messageProcessor != null) { + Object result = this.messageProcessor.processMessage(message); + if (result instanceof Map) { + Map resultMap = (Map) result; + for (Object key : resultMap.keySet()) { + if (key instanceof String) { + if (this.defaultOverwrite || headerMap.get(key) == null) { + headerMap.put((String) key, resultMap.get(key)); + } + } + else if (logger.isDebugEnabled()) { + logger.debug("ignoring value for non-String key: " + key); + } + } + } + else if (logger.isDebugEnabled()) { + logger.debug("expected a Map result from processor, but received: " + result); + } + } + } public static interface ValueHolder { 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 cc7c0458dc..41b02408ab 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 @@ -1261,6 +1261,28 @@ + + + + Reference to an Object to be invoked for header values. + The 'method' attribute is required along with this. + + + + + + + + + + Method to be invoked on the referenced Object as specified by the + 'ref' attribute. The method should return a Map with String-typed keys. + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml new file mode 100644 index 0000000000..a86fed97cf --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests-context.xml @@ -0,0 +1,16 @@ + + + + +
+ + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests.java new file mode 100644 index 0000000000..e34c17e642 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/config/xml/HeaderEnricherMethodInvokingTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2010 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.config.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.Message; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + * @since 2.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class HeaderEnricherMethodInvokingTests { + + @Autowired + private ApplicationContext context; + + + @Test + public void replyChannelExplicitOverwriteTrue() { + MessageChannel inputChannel = context.getBean("input", MessageChannel.class); + QueueChannel replyChannel = new QueueChannel(); + Message message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build(); + inputChannel.send(message); + Message result = replyChannel.receive(0); + assertNotNull(result); + assertEquals("test", result.getPayload()); + assertEquals(replyChannel, result.getHeaders().getReplyChannel()); + assertEquals(123, result.getHeaders().get("foo")); + assertEquals("ABC", result.getHeaders().get("bar")); + assertEquals("zzz", result.getHeaders().get("other")); + } + + + public static class TestBean { + + public String echo(String text) { + return text.toUpperCase(); + } + + public Map enrich() { + Map headers = new HashMap(); + headers.put("foo", 123); + headers.put("bar", "ABC"); + return headers; + } + } + +} diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/MethodInvokingHeaderEnricherTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/MethodInvokingHeaderEnricherTests.java new file mode 100644 index 0000000000..d00bf2c70e --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/handler/MethodInvokingHeaderEnricherTests.java @@ -0,0 +1,99 @@ +/* + * Copyright 2002-2010 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.handler; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import org.springframework.integration.annotation.Payload; +import org.springframework.integration.core.Message; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.transformer.HeaderEnricher; + +/** + * @author Mark Fisher + * @since 2.0 + */ +public class MethodInvokingHeaderEnricherTests { + + @Test + public void emptyHeadersOnRequest() { + TestBean testBean = new TestBean(); + MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testBean, "process"); + HeaderEnricher enricher = new HeaderEnricher(); + enricher.setMessageProcessor(processor); + enricher.setDefaultOverwrite(true); + Message message = MessageBuilder.withPayload("test").build(); + Message result = enricher.transform(message); + assertEquals("TEST", result.getHeaders().get("foo")); + assertEquals("ABC", result.getHeaders().get("bar")); + } + + @Test + public void overwriteFalseByDefault() { + TestBean testBean = new TestBean(); + MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testBean, "process"); + HeaderEnricher enricher = new HeaderEnricher(); + enricher.setMessageProcessor(processor); + Message message = MessageBuilder.withPayload("test").setHeader("bar", "XYZ").build(); + Message result = enricher.transform(message); + assertEquals("TEST", result.getHeaders().get("foo")); + assertEquals("XYZ", result.getHeaders().get("bar")); + } + + @Test + public void overwriteFalseExplicit() { + TestBean testBean = new TestBean(); + MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testBean, "process"); + HeaderEnricher enricher = new HeaderEnricher(); + enricher.setMessageProcessor(processor); + enricher.setDefaultOverwrite(false); + Message message = MessageBuilder.withPayload("test").setHeader("bar", "XYZ").build(); + Message result = enricher.transform(message); + assertEquals("TEST", result.getHeaders().get("foo")); + assertEquals("XYZ", result.getHeaders().get("bar")); + } + + @Test + public void overwriteTrue() { + TestBean testBean = new TestBean(); + MethodInvokingMessageProcessor processor = new MethodInvokingMessageProcessor(testBean, "process"); + HeaderEnricher enricher = new HeaderEnricher(); + enricher.setMessageProcessor(processor); + enricher.setDefaultOverwrite(true); + Message message = MessageBuilder.withPayload("test").setHeader("bar", "XYZ").build(); + Message result = enricher.transform(message); + assertEquals("TEST", result.getHeaders().get("foo")); + assertEquals("ABC", result.getHeaders().get("bar")); + } + + + public static class TestBean { + + public Map process(@Payload("toUpperCase()") String s) { + Map map = new HashMap(); + map.put("foo", s); + map.put("bar", "ABC"); + return map; + } + } + +}