INT-1136 the 'router' element now supports 'mapping' sub-elements (mapping the returned 'value' to a 'channel' reference)

This commit is contained in:
Mark Fisher
2010-05-08 23:29:57 +00:00
parent 58c5138ec0
commit bd5fadf6d0
9 changed files with 305 additions and 59 deletions

View File

@@ -99,13 +99,14 @@ public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean {
MethodInvokingRouter router = (StringUtils.hasText(targetMethodName))
? new MethodInvokingRouter(targetObject, targetMethodName)
: new MethodInvokingRouter(targetObject);
if (this.channelResolver != null) {
router.setChannelResolver(this.channelResolver);
}
return router;
}
private AbstractMessageRouter configureRouter(AbstractMessageRouter router) {
if (this.channelResolver != null &&
router instanceof AbstractChannelNameResolvingMessageRouter) {
((AbstractChannelNameResolvingMessageRouter) router).setChannelResolver(this.channelResolver);
}
if (this.defaultOutputChannel != null) {
router.setDefaultOutputChannel(this.defaultOutputChannel);
}

View File

@@ -0,0 +1,60 @@
/*
* 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 java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
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.util.xml.DomUtils;
/**
* Base parser for routers that create instances that are subclasses of AbstractChannelNameResolvingMessageRouter.
*
* @author Mark Fisher
*/
public abstract class AbstractChannelNameResolvingRouterParser extends AbstractRouterParser {
@Override
protected final BeanDefinition parseRouter(Element element, ParserContext parserContext) {
BeanDefinition beanDefinition = this.doParseRouter(element, parserContext);
if (beanDefinition != null) {
// check if mapping is provided otherwise returned values will be treated as channel names
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
if (childElements != null && childElements.size() > 0) {
BeanDefinitionBuilder channelResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.MapBasedChannelResolver");
ManagedMap<String, RuntimeBeanReference> channelMap = new ManagedMap<String, RuntimeBeanReference>();
for (Element childElement : childElements) {
channelMap.put(childElement.getAttribute("value"),
new RuntimeBeanReference(childElement.getAttribute("channel")));
}
channelResolverBuilder.addPropertyValue("channelMap", channelMap);
beanDefinition.getPropertyValues().add("channelResolver", channelResolverBuilder.getBeanDefinition());
}
}
return beanDefinition;
}
protected abstract BeanDefinition doParseRouter(Element element, ParserContext parserContext);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
@@ -38,10 +39,11 @@ public abstract class AbstractRouterParser extends AbstractConsumerEndpointParse
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-send-failures");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-channel-name-resolution-failures");
this.parseRouter(element, builder, parserContext);
BeanDefinition targetRouterBeanDefinition = this.parseRouter(element, parserContext);
builder.addPropertyValue("targetObject", targetRouterBeanDefinition);
return builder;
}
protected abstract void parseRouter(Element element, BeanDefinitionBuilder rootBuilder, ParserContext parserContext);
protected abstract BeanDefinition parseRouter(Element element, ParserContext parserContext);
}

View File

@@ -16,12 +16,18 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the &lt;router/&gt; element.
@@ -30,6 +36,9 @@ import org.springframework.util.StringUtils;
*/
public class DefaultRouterParser extends AbstractDelegatingConsumerEndpointParser {
private static final String CHANNEL_RESOLVER_PROPERTY = "channelResolver";
@Override
String getFactoryBeanClassName() {
return IntegrationNamespaceUtils.BASE_PACKAGE + ".config.RouterFactoryBean";
@@ -43,13 +52,31 @@ public class DefaultRouterParser extends AbstractDelegatingConsumerEndpointParse
@Override
protected void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
String resolverBeanName = element.getAttribute("channel-resolver");
if (!StringUtils.hasText(resolverBeanName)) {
BeanDefinitionBuilder resolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.BeanFactoryChannelResolver");
resolverBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
resolverBuilder.getBeanDefinition(), parserContext.getRegistry());
List<Element> mappingElements = DomUtils.getChildElementsByTagName(element, "mapping");
if (!CollectionUtils.isEmpty(mappingElements)) {
if (StringUtils.hasText(resolverBeanName)) {
parserContext.getReaderContext().error(
"The 'channel-resolver' attribute and 'mapping' sub-elements are mutually exclusive.",
parserContext.extractSource(element));
}
BeanDefinitionBuilder channelResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.MapBasedChannelResolver");
ManagedMap<String, RuntimeBeanReference> channelMap = new ManagedMap<String, RuntimeBeanReference>();
for (Element mappingElement : mappingElements) {
channelMap.put(mappingElement.getAttribute("value"),
new RuntimeBeanReference(mappingElement.getAttribute("channel")));
}
channelResolverBuilder.addPropertyValue("channelMap", channelMap);
builder.addPropertyValue(CHANNEL_RESOLVER_PROPERTY, channelResolverBuilder.getBeanDefinition());
}
else if (StringUtils.hasText(resolverBeanName)) {
builder.addPropertyReference(CHANNEL_RESOLVER_PROPERTY, resolverBeanName);
}
else {
RootBeanDefinition resolverBeanDefintion = new RootBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.BeanFactoryChannelResolver");
builder.addPropertyValue(CHANNEL_RESOLVER_PROPERTY, resolverBeanDefintion);
}
builder.addPropertyReference("channelResolver", resolverBeanName);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "default-output-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "resolution-required");

View File

@@ -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,16 +16,11 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the &lt;header-value-router/&gt; element.
@@ -34,30 +29,14 @@ import org.springframework.util.xml.DomUtils;
* @author Mark Fisher
* @since 1.0.3
*/
public class HeaderValueRouterParser extends AbstractRouterParser {
public class HeaderValueRouterParser extends AbstractChannelNameResolvingRouterParser {
@Override
@SuppressWarnings("unchecked")
protected void parseRouter(Element element, BeanDefinitionBuilder rootBuilder, ParserContext parserContext) {
protected BeanDefinition doParseRouter(Element element, ParserContext parserContext) {
BeanDefinitionBuilder headerValueRouterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".router.HeaderValueRouter");
headerValueRouterBuilder.addConstructorArgValue(element.getAttribute("header-name"));
// check if mapping is provided otherwise header values will be treated as channel names
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
if (childElements != null && childElements.size() > 0) {
BeanDefinitionBuilder mapBasedChannelResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.MapBasedChannelResolver");
ManagedMap channelMap = new ManagedMap();
for (Element childElement : childElements) {
channelMap.put(childElement.getAttribute("value"),
new RuntimeBeanReference(childElement.getAttribute("channel")));
}
mapBasedChannelResolverBuilder.addPropertyValue("channelMap", channelMap);
String resolverBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
mapBasedChannelResolverBuilder.getBeanDefinition(), parserContext.getRegistry());
headerValueRouterBuilder.addPropertyReference("channelResolver", resolverBeanName);
}
rootBuilder.addPropertyValue("targetObject", headerValueRouterBuilder.getBeanDefinition());
return headerValueRouterBuilder.getBeanDefinition();
}
}

View File

@@ -20,6 +20,7 @@ import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
@@ -39,7 +40,7 @@ public class PayloadTypeRouterParser extends AbstractRouterParser {
@Override
@SuppressWarnings("unchecked")
protected void parseRouter(Element element, BeanDefinitionBuilder rootBuilder, ParserContext parserContext) {
protected BeanDefinition parseRouter(Element element, ParserContext parserContext) {
BeanDefinitionBuilder payloadTypeRouterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".router.PayloadTypeRouter");
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
@@ -56,7 +57,7 @@ public class PayloadTypeRouterParser extends AbstractRouterParser {
channelMap.put(typeName, new RuntimeBeanReference(childElement.getAttribute("channel")));
}
payloadTypeRouterBuilder.addPropertyValue("payloadTypeChannelMap", channelMap);
rootBuilder.addPropertyValue("targetObject", payloadTypeRouterBuilder.getBeanDefinition());
return payloadTypeRouterBuilder.getBeanDefinition();
}
}

View File

@@ -1578,23 +1578,6 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="channelNameResolvingRouterType">
<xsd:sequence>
<xsd:element name="mapping" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:string" />
<xsd:attribute name="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:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="header-name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -1679,11 +1662,11 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="channelNameResolvingRouterType">
<xsd:all>
<xsd:sequence>
<xsd:element name="poller" type="innerPollerType"
minOccurs="0" maxOccurs="1" />
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:all>
</xsd:sequence>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -1726,6 +1709,23 @@
<xsd:complexType name="channelNameResolvingRouterType">
<xsd:complexContent>
<xsd:extension base="channelResolvingRouterType">
<xsd:sequence>
<xsd:element name="mapping" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="value" type="xsd:string" />
<xsd:attribute name="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:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="ignore-channel-name-resolution-failures"
type="xsd:string">
<xsd:annotation>