Added the 'header-enricher' element to the core namespace (INT-489).

This commit is contained in:
Mark Fisher
2008-11-22 15:43:39 +00:00
parent d943af0e87
commit abb370b257
7 changed files with 179 additions and 29 deletions

View File

@@ -105,6 +105,7 @@ public class IntegrationNamespaceHandler implements NamespaceHandler {
registerBeanDefinitionParser("splitter", new SplitterParser());
registerBeanDefinitionParser("aggregator", new AggregatorParser());
registerBeanDefinitionParser("resequencer", new ResequencerParser());
registerBeanDefinitionParser("header-enricher", new StandardHeaderEnricherParser());
registerBeanDefinitionParser("inbound-channel-adapter", new MethodInvokingInboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-channel-adapter", new MethodInvokingOutboundChannelAdapterParser());
registerBeanDefinitionParser("gateway", new GatewayParser());

View File

@@ -84,20 +84,18 @@ public class SimpleHeaderEnricherParser extends AbstractTransformerParser {
Node node = attributes.item(i);
String name = node.getNodeName();
if (this.isEligibleHeaderName(name)) {
if (this.referenceAttributes.contains(name)) {
headers.put(name, new RuntimeBeanReference(node.getNodeValue()));
}
else {
headers.put(name, node.getNodeValue());
Object value = (this.referenceAttributes.contains(name))
? new RuntimeBeanReference(node.getNodeValue())
: node.getNodeValue();
if (this.prefix != null) {
name = this.prefix + name;
}
headers.put(name, value);
}
}
this.postProcessHeaders(element, headers);
builder.addConstructorArgValue(headers);
builder.addPropertyValue("overwrite", this.shouldOverwrite(element));
if (this.prefix != null) {
builder.addPropertyValue("prefix", this.prefix);
}
}
/**

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2002-2008 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 org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.integration.core.MessageHeaders;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Parser for the <header-enricher> element within the core integration
* namespace. This is used for setting the <em>standard</em>, out-of-the-box
* configurable {@link MessageHeaders}, such as 'reply-channel', 'priority',
* and 'correlation-id'. It will also accept custom header values (or bean
* references) if provided as 'header' sub-elements.
*
* @author Mark Fisher
*/
public class StandardHeaderEnricherParser extends SimpleHeaderEnricherParser {
private static final String[] REFERENCE_ATTRIBUTES = new String[] {
"reply-channel", "error-channel"
};
public StandardHeaderEnricherParser() {
super(MessageHeaders.PREFIX, REFERENCE_ATTRIBUTES);
}
@Override
@SuppressWarnings("unchecked")
protected void postProcessHeaders(Element element, ManagedMap headers) {
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE && node.getLocalName().equals("header")) {
Element headerElement = (Element) node;
String name = headerElement.getAttribute("name");
String value = headerElement.getAttribute("value");
String ref = headerElement.getAttribute("ref");
boolean isValue = StringUtils.hasText(value);
boolean isRef = StringUtils.hasText(ref);
Assert.isTrue(isValue ^ isRef, "Exactly one of the 'value' or 'ref' attributes is required.");
if (isValue) {
headers.put(name, value);
}
else {
headers.put(name, new RuntimeBeanReference(ref));
}
}
}
}
}

View File

@@ -266,6 +266,14 @@
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="header-enricher">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="header" type="headerElementType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attributeGroup ref="headerEnricherAttributes"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="transformer" type="handlerType"/>
<xsd:element name="service-activator" type="handlerType"/>
<xsd:element name="splitter" type="handlerType"/>
@@ -387,6 +395,48 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="header-enricher">
<xsd:annotation>
<xsd:documentation>
Defines a HeaderEnricher endpoint for values defined in the MessageHeaders.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="inputOutputEndpointType">
<xsd:sequence>
<xsd:element name="header" type="headerElementType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attributeGroup ref="headerEnricherAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="headerElementType">
<xsd:annotation>
<xsd:documentation>
Defines a Message Header value or ref.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="ref" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</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="reply-channel" type="xsd:string"/>
<xsd:attribute name="error-channel" type="xsd:string"/>
<xsd:attribute name="correlation-id" type="xsd:string"/>
<xsd:attribute name="expiration-date" type="xsd:string"/>
<xsd:attribute name="priority" type="xsd:string"/>
</xsd:attributeGroup>
<xsd:element name="thread-pool-task-executor">
<xsd:complexType>
<xsd:annotation>

View File

@@ -22,8 +22,8 @@ import org.springframework.util.Assert;
/**
* A Transformer that adds statically configured header values to a Message.
* Accepts an optional 'prefix' String and a boolean that specifies whether
* values should be overwritten. By default, any existing header values for
* Accepts the boolean 'overwrite' property that specifies whether values
* should be overwritten. By default, any existing header values for
* a given key, will <em>not</em> be replaced.
*
* @author Mark Fisher
@@ -34,8 +34,6 @@ public class HeaderEnricher extends AbstractHeaderTransformer {
private volatile boolean overwrite;
private volatile String prefix;
/**
* Create a HeaderEnricher with the given map of headers.
@@ -50,17 +48,10 @@ public class HeaderEnricher extends AbstractHeaderTransformer {
this.overwrite = overwrite;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
@Override
protected final void transformHeaders(Map<String, Object> headers) {
for (Map.Entry<String, Object> entry : this.headersToAdd.entrySet()) {
String key = entry.getKey();
if (this.prefix != null) {
key = this.prefix + key;
}
if (this.overwrite || headers.get(key) == null) {
headers.put(key, entry.getValue());
}

View File

@@ -7,17 +7,33 @@
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">
<channel id="input"/>
<channel id="filterInput"/>
<channel id="output">
<queue/>
</channel>
<chain input-channel="input" output-channel="output">
<channel id="replyOutput">
<queue/>
</channel>
<chain input-channel="filterInput" output-channel="output">
<filter ref="typeSelector"/>
<service-activator ref="testHandler"/>
</chain>
<chain input-channel="headerEnricherInput">
<header-enricher reply-channel="replyOutput" correlation-id="ABC">
<header name="testValue" value="XYZ"/>
<header name="testRef" ref="testHeaderValue"/>
</header-enricher>
<service-activator ref="testHandler"/>
</chain>
<beans:bean id="testHeaderValue" class="java.lang.Integer">
<beans:constructor-arg value="123"/>
</beans:bean>
<beans:bean id="typeSelector" class="org.springframework.integration.selector.PayloadTypeSelector">
<beans:constructor-arg value="java.lang.String"/>
</beans:bean>

View File

@@ -38,19 +38,27 @@ import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
public class ChainParserTests extends AbstractJUnit4SpringContextTests {
@Autowired
@Qualifier("input")
private MessageChannel inputChannel;
@Qualifier("filterInput")
private MessageChannel filterInput;
@Autowired
@Qualifier("headerEnricherInput")
private MessageChannel headerEnricherInput;
@Autowired
@Qualifier("output")
private PollableChannel outputChannel;
private PollableChannel output;
@Autowired
@Qualifier("replyOutput")
private PollableChannel replyOutput;
@Test
public void testChainWithAcceptingFilter() {
public void chainWithAcceptingFilter() {
Message<?> message = MessageBuilder.withPayload("test").build();
this.inputChannel.send(message);
Message<?> reply = this.outputChannel.receive(0);
this.filterInput.send(message);
Message<?> reply = this.output.receive(0);
assertNotNull(reply);
assertEquals("foo", reply.getPayload());
}
@@ -58,9 +66,21 @@ public class ChainParserTests extends AbstractJUnit4SpringContextTests {
@Test
public void chainWithRejectingFilter() {
Message<?> message = MessageBuilder.withPayload(123).build();
this.inputChannel.send(message);
Message<?> reply = this.outputChannel.receive(0);
this.filterInput.send(message);
Message<?> reply = this.output.receive(0);
assertNull(reply);
}
@Test
public void chainWithHeaderEnricher() {
Message<?> message = MessageBuilder.withPayload(123).build();
this.headerEnricherInput.send(message);
Message<?> reply = this.replyOutput.receive(0);
assertNotNull(reply);
assertEquals("foo", reply.getPayload());
assertEquals("ABC", reply.getHeaders().getCorrelationId());
assertEquals("XYZ", reply.getHeaders().get("testValue"));
assertEquals(123, reply.getHeaders().get("testRef"));
}
}