INT-712 Headers defined by 'header-enricher' elements are now configured as sub-elements rather than attributes. This applies to the "core" as well as the JMS and Mail namespace support.

This commit is contained in:
Mark Fisher
2009-11-03 00:14:28 +00:00
parent 543ec6f5f6
commit 33f593cf27
18 changed files with 550 additions and 215 deletions

View File

@@ -16,6 +16,10 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -23,8 +27,8 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Base class parser for elements that create Message Endpoints.
@@ -97,9 +101,13 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
}
builder.addPropertyValue("inputChannelName", inputChannelName);
Element pollerElement = DomUtils.getChildElementByTagName(element, "poller");
if (pollerElement != null) {
IntegrationNamespaceUtils.configurePollerMetadata(pollerElement, builder, parserContext);
List<Element> pollerElementList = DomUtils.getChildElementsByTagName(element, "poller");
if (!CollectionUtils.isEmpty(pollerElementList)) {
if (pollerElementList.size() != 1) {
parserContext.getReaderContext().error(
"at most one poller element may be configured for an endpoint", element);
}
IntegrationNamespaceUtils.configurePollerMetadata(pollerElementList.get(0), builder, parserContext);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
return builder.getBeanDefinition();

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2002-2009 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 java.util.HashMap;
import java.util.Map;
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.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.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Base support class for 'header-enricher' parsers.
*
* @author Mark Fisher
* @since 2.0
*/
public abstract class HeaderEnricherParserSupport extends AbstractTransformerParser {
private final Map<String, String> elementToNameMap = new HashMap<String, String>();
private final Map<String, Class<?>> elementToTypeMap = new HashMap<String, Class<?>>();
@Override
protected final String getTransformerClassName() {
return IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher";
}
protected boolean shouldOverwrite(Element element) {
return "true".equals(element.getAttribute("overwrite").toLowerCase());
}
protected final void addElementToHeaderMapping(String elementName, String headerName) {
this.addElementToHeaderMapping(elementName, headerName, null);
}
protected final void addElementToHeaderMapping(String elementName, String headerName, Class<?> headerType) {
this.elementToNameMap.put(elementName, headerName);
if (headerType != null) {
this.elementToTypeMap.put(elementName, headerType);
}
}
@Override
@SuppressWarnings("unchecked")
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
ManagedMap headers = new ManagedMap();
this.processHeaders(element, headers, parserContext);
builder.addConstructorArgValue(headers);
builder.addPropertyValue("overwrite", this.shouldOverwrite(element)); // TODO: should be a per-header config setting
}
protected void processHeaders(Element element, ManagedMap<String, Object> headers, ParserContext parserContext) {
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String headerName = null;
Element headerElement = (Element) node;
String elementName = node.getLocalName();
Class<?> headerType = null;
if ("header".equals(elementName)) {
headerName = headerElement.getAttribute("name");
String headerTypeName = headerElement.getAttribute("type");
if (StringUtils.hasText(headerTypeName)) {
ClassLoader classLoader = parserContext.getReaderContext().getBeanClassLoader();
if (classLoader != null) {
try {
headerType = ClassUtils.forName(headerTypeName, classLoader);
}
catch (Exception e) {
parserContext.getReaderContext().error("unable to resolve type [" +
headerTypeName + "] for header '" + headerName + "'", element, e);
}
}
}
}
else {
headerName = elementToNameMap.get(elementName);
headerType = elementToTypeMap.get(elementName);
}
if (headerName != null) {
String value = headerElement.getAttribute("value");
String ref = headerElement.getAttribute("ref");
boolean isValue = StringUtils.hasText(value);
boolean isRef = StringUtils.hasText(ref);
if (!(isValue ^ isRef)) {
parserContext.getReaderContext().error(
"Exactly one of the 'value' or 'ref' attributes is required.", element);
}
if (isValue) {
Object headerValue = (headerType != null) ?
new TypedStringValue(value, headerType) : value;
headers.put(headerName, headerValue);
}
else {
headers.put(headerName, new RuntimeBeanReference(ref));
}
}
}
}
}
}

View File

@@ -1,107 +0,0 @@
/*
* 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 java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.Conventions;
import org.springframework.util.ObjectUtils;
/**
* @author Mark Fisher
*/
public class SimpleHeaderEnricherParser extends AbstractTransformerParser {
private static String[] ineligibleHeaderNames = new String[] {
"id", "input-channel", "output-channel", "overwrite"
};
private final String prefix;
private List<String> referenceAttributes;
public SimpleHeaderEnricherParser() {
this(null, null);
}
public SimpleHeaderEnricherParser(String prefix) {
this(prefix, null);
}
public SimpleHeaderEnricherParser(String prefix, String[] referenceAttributes) {
this.prefix = prefix;
this.referenceAttributes = (referenceAttributes != null)
? Arrays.asList(referenceAttributes) : Collections.<String>emptyList();
}
@Override
protected String getTransformerClassName() {
return IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher";
}
protected boolean isEligibleHeaderName(String headerName) {
return !(ObjectUtils.containsElement(ineligibleHeaderNames, headerName));
}
protected boolean shouldOverwrite(Element element) {
return "true".equals(element.getAttribute("overwrite").toLowerCase());
}
@Override
@SuppressWarnings("unchecked")
protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
ManagedMap headers = new ManagedMap();
NamedNodeMap attributes = element.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
String name = node.getNodeName();
if (this.isEligibleHeaderName(name)) {
name = Conventions.attributeNameToPropertyName(name);
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, parserContext);
builder.addConstructorArgValue(headers);
builder.addPropertyValue("overwrite", this.shouldOverwrite(element));
}
/**
* Subclasses may implement this method to provide additional headers.
*/
protected void postProcessHeaders(Element element, ManagedMap headers, ParserContext parserContext) {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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,15 +16,8 @@
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.beans.factory.xml.ParserContext;
import org.springframework.integration.core.MessageHeaders;
import org.springframework.util.StringUtils;
import org.springframework.integration.core.MessagePriority;
/**
* Parser for the &lt;header-enricher&gt; element within the core integration
@@ -35,43 +28,14 @@ import org.springframework.util.StringUtils;
*
* @author Mark Fisher
*/
public class StandardHeaderEnricherParser extends SimpleHeaderEnricherParser {
private static final String[] REFERENCE_ATTRIBUTES = new String[] {
"reply-channel", "error-channel"
};
public class StandardHeaderEnricherParser extends HeaderEnricherParserSupport {
public StandardHeaderEnricherParser() {
super(MessageHeaders.PREFIX, REFERENCE_ATTRIBUTES);
}
@Override
@SuppressWarnings("unchecked")
protected void postProcessHeaders(Element element, ManagedMap headers, ParserContext parserContext) {
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);
if (!(isValue ^ isRef)) {
parserContext.getReaderContext().error(
"Exactly one of the 'value' or 'ref' attributes is required.", element);
}
if (isValue) {
headers.put(name, value);
}
else {
headers.put(name, new RuntimeBeanReference(ref));
}
}
}
this.addElementToHeaderMapping("reply-channel", MessageHeaders.REPLY_CHANNEL);
this.addElementToHeaderMapping("error-channel", MessageHeaders.ERROR_CHANNEL);
this.addElementToHeaderMapping("correlation-id", MessageHeaders.CORRELATION_ID);
this.addElementToHeaderMapping("expiration-date", MessageHeaders.EXPIRATION_DATE, Long.class);
this.addElementToHeaderMapping("priority", MessageHeaders.PRIORITY, MessagePriority.class);
}
}

View File

@@ -878,25 +878,87 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="inputOutputEndpointType">
<xsd:sequence>
<xsd:element name="header" type="headerElementType"
minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:choice minOccurs="1" maxOccurs="unbounded">
<xsd:element name="reply-channel" type="referenceHeaderType"/>
<xsd:element name="error-channel" type="referenceHeaderType"/>
<xsd:element name="correlation-id" type="referenceOrValueHeaderType"/>
<xsd:element name="expiration-date" type="referenceOrValueHeaderType"/>
<xsd:element name="priority">
<xsd:complexType>
<xsd:attribute name="value">
<xsd:simpleType>
<xsd:union memberTypes="priorityEnumeration xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="header" type="userDefinedHeaderType">
<xsd:annotation>
<xsd:documentation>
Element that accepts any user-defined header name/value pair.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attributeGroup ref="headerEnricherAttributes" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="headerElementType">
<xsd:simpleType name="priorityEnumeration">
<xsd:restriction base="xsd:token">
<xsd:enumeration value="HIGHEST"/>
<xsd:enumeration value="HIGH"/>
<xsd:enumeration value="NORMAL"/>
<xsd:enumeration value="LOW"/>
<xsd:enumeration value="LOWEST"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="userDefinedHeaderType">
<xsd:annotation>
<xsd:documentation>
Defines a Message Header value or ref.
Defines a Message Header with a literal value or object reference.
</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:complexContent>
<xsd:extension base="referenceOrValueHeaderType">
<xsd:attribute name="name" use="required" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Name of the header to be added.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="referenceOrValueHeaderType">
<xsd:complexContent>
<xsd:extension base="referenceHeaderType">
<xsd:attribute name="value" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Literal value to be associated with the given header name.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<!-- TODO: xsd:attribute name="expression" type="xsd:string" / -->
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="referenceHeaderType">
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Reference to be associated with the given header name.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:attributeGroup name="headerEnricherAttributes">
@@ -906,29 +968,6 @@
MessageHeaders.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="correlation-id" type="xsd:string" />
<xsd:attribute name="expiration-date" type="xsd:string" />
<xsd:attribute name="priority" type="xsd:string" />
<xsd:attribute name="overwrite" type="xsd:string" />
</xsd:attributeGroup>