Refactored ChannelParser into a hierarchy of parsers. Added namespace support for "rendezvous-channel" and "direct-channel" (a.k.a. SyncrhonousChannel at this point).

This commit is contained in:
Mark Fisher
2008-04-28 23:59:43 +00:00
parent b85d38641c
commit 369ea90b24
19 changed files with 425 additions and 44 deletions

View File

@@ -14,36 +14,30 @@
* limitations under the License.
*/
package org.springframework.integration.config;
package org.springframework.integration.channel.config;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.channel.DispatcherPolicy;
import org.springframework.integration.channel.PriorityChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.interceptor.MessageSelectingInterceptor;
import org.springframework.integration.message.selector.PayloadTypeSelector;
import org.springframework.util.StringUtils;
/**
* Parser for the <em>channel</em> element of the integration namespace.
* Base class for channel parsers.
*
* @author Mark Fisher
*/
public class ChannelParser implements BeanDefinitionParser {
private static final String ID_ATTRIBUTE = "id";
private static final String CAPACITY_ATTRIBUTE = "capacity";
public abstract class AbstractChannelParser extends AbstractSingleBeanDefinitionParser {
private static final String PUBLISH_SUBSCRIBE_ATTRIBUTE = "publish-subscribe";
@@ -55,14 +49,25 @@ public class ChannelParser implements BeanDefinitionParser {
private static final String INTERCEPTORS_PROPERTY = "interceptors";
private static final String COMPARATOR_REF_ATTRIBUTE = "comparator-ref";
@Override
protected boolean shouldGenerateId() {
return false;
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
boolean isPriorityChannel = (element.getLocalName().equals("priority-channel"));
Class<?> channelClass = (isPriorityChannel) ? PriorityChannel.class : QueueChannel.class;
RootBeanDefinition channelDef = new RootBeanDefinition(channelClass);
channelDef.setSource(parserContext.extractSource(element));
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected abstract Class<?> getBeanClass(Element element);
protected abstract void configureConstructorArgs(
BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy);
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
boolean isPublishSubscribe = "true".equals(element.getAttribute(PUBLISH_SUBSCRIBE_ATTRIBUTE));
DispatcherPolicy dispatcherPolicy = new DispatcherPolicy(isPublishSubscribe);
ManagedList interceptors = new ManagedList();
@@ -79,16 +84,6 @@ public class ChannelParser implements BeanDefinitionParser {
interceptors.add(new RuntimeBeanReference(ref));
}
}
}
String capAttr = element.getAttribute(CAPACITY_ATTRIBUTE);
int capacity = (StringUtils.hasText(capAttr)) ? Integer.parseInt(capAttr) : QueueChannel.DEFAULT_CAPACITY;
channelDef.getConstructorArgumentValues().addIndexedArgumentValue(0, capacity);
channelDef.getConstructorArgumentValues().addIndexedArgumentValue(1, dispatcherPolicy);
if (isPriorityChannel) {
String comparatorRef = element.getAttribute(COMPARATOR_REF_ATTRIBUTE);
if (StringUtils.hasText(comparatorRef)) {
channelDef.getConstructorArgumentValues().addIndexedArgumentValue(2, new RuntimeBeanReference(comparatorRef));
}
}
String datatypeAttr = element.getAttribute(DATATYPE_ATTRIBUTE);
if (StringUtils.hasText(datatypeAttr)) {
@@ -105,10 +100,8 @@ public class ChannelParser implements BeanDefinitionParser {
parserContext.registerBeanComponent(interceptorComponent);
interceptors.add(new RuntimeBeanReference(interceptorBeanName));
}
channelDef.getPropertyValues().addPropertyValue(INTERCEPTORS_PROPERTY, interceptors);
String beanName = element.getAttribute(ID_ATTRIBUTE);
parserContext.registerBeanComponent(new BeanComponentDefinition(channelDef, beanName));
return channelDef;
builder.addPropertyValue(INTERCEPTORS_PROPERTY, interceptors);
this.configureConstructorArgs(builder, element, dispatcherPolicy);
}
private void configureDispatcherPolicy(Element element, DispatcherPolicy dispatcherPolicy) {

View File

@@ -0,0 +1,46 @@
/*
* 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.channel.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.channel.DispatcherPolicy;
import org.springframework.integration.dispatcher.SynchronousChannel;
import org.springframework.util.StringUtils;
/**
* Parser for the &lt;direct-channel&gt; element.
*
* @author Mark Fisher
*/
public class DirectChannelParser extends AbstractChannelParser {
@Override
protected Class<?> getBeanClass(Element element) {
return SynchronousChannel.class;
}
@Override
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
String source = element.getAttribute("source");
if (StringUtils.hasText(source)) {
builder.addConstructorArgReference(source);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.channel.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.channel.DispatcherPolicy;
import org.springframework.integration.channel.PriorityChannel;
import org.springframework.util.StringUtils;
/**
* Parser for the &lt;priority-channel&gt; element.
*
* @author Mark Fisher
*/
public class PriorityChannelParser extends QueueChannelParser {
@Override
protected Class<?> getBeanClass(Element element) {
return PriorityChannel.class;
}
@Override
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
super.configureConstructorArgs(builder, element, dispatcherPolicy);
String comparator = element.getAttribute("comparator");
if (StringUtils.hasText(comparator)) {
builder.addConstructorArgReference(comparator);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.channel.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.channel.DispatcherPolicy;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.util.StringUtils;
/**
* Parser for the &lt;channel&gt; element.
*
* @author Mark Fisher
*/
public class QueueChannelParser extends AbstractChannelParser {
@Override
protected Class<?> getBeanClass(Element element) {
return QueueChannel.class;
}
@Override
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
String capacityAttribute = element.getAttribute("capacity");
int capacity = (StringUtils.hasText(capacityAttribute)) ?
Integer.parseInt(capacityAttribute) : QueueChannel.DEFAULT_CAPACITY;
builder.addConstructorArgValue(capacity);
builder.addConstructorArgValue(dispatcherPolicy);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.channel.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.channel.DispatcherPolicy;
import org.springframework.integration.channel.RendezvousChannel;
/**
* Parser for the &lt;rendezvous-channel&gt; element.
*
* @author Mark Fisher
*/
public class RendezvousChannelParser extends AbstractChannelParser {
@Override
protected Class<?> getBeanClass(Element element) {
return RendezvousChannel.class;
}
@Override
protected void configureConstructorArgs(BeanDefinitionBuilder builder, Element element, DispatcherPolicy dispatcherPolicy) {
builder.addConstructorArgValue(dispatcherPolicy);
}
}

View File

@@ -28,6 +28,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.integration.channel.config.DirectChannelParser;
import org.springframework.integration.channel.config.PriorityChannelParser;
import org.springframework.integration.channel.config.QueueChannelParser;
import org.springframework.integration.channel.config.RendezvousChannelParser;
import org.springframework.util.ClassUtils;
/**
@@ -47,8 +51,10 @@ public class IntegrationNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("message-bus", new MessageBusParser());
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenParser());
registerBeanDefinitionParser("channel", new ChannelParser());
registerBeanDefinitionParser("priority-channel", new ChannelParser());
registerBeanDefinitionParser("channel", new QueueChannelParser());
registerBeanDefinitionParser("direct-channel", new DirectChannelParser());
registerBeanDefinitionParser("priority-channel", new PriorityChannelParser());
registerBeanDefinitionParser("rendezvous-channel", new RendezvousChannelParser());
registerBeanDefinitionParser("source-adapter", new MethodInvokingAdapterParser());
registerBeanDefinitionParser("target-adapter", new MethodInvokingAdapterParser());
registerBeanDefinitionParser("source-endpoint", new SourceEndpointParser());

View File

@@ -44,7 +44,28 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="channel" type="channelType"/>
<xsd:element name="channel" type="capacityChannelType">
<xsd:annotation>
<xsd:documentation>
Defines a channel that buffers messages in a queue.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="direct-channel">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines a channel that invokes its handlers directly in the sender's thread.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="channelType">
<xsd:attribute name="source" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="priority-channel">
<xsd:complexType>
@@ -54,13 +75,28 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="channelType">
<xsd:attribute name="comparator-ref" type="xsd:string"/>
<xsd:extension base="capacityChannelType">
<xsd:attribute name="comparator" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="rendezvous-channel" type="channelType"/>
<xsd:complexType name="capacityChannelType">
<xsd:annotation>
<xsd:documentation>
Defines a channel with a configurable capacity.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="channelType">
<xsd:attribute name="capacity" type="xsd:integer"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="channelType">
<xsd:annotation>
<xsd:documentation>
@@ -72,7 +108,6 @@
<xsd:element ref="interceptor" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" use="required"/>
<xsd:attribute name="capacity" type="xsd:integer"/>
<xsd:attribute name="publish-subscribe" type="xsd:boolean" default="false"/>
<xsd:attribute name="datatype" type="xsd:string"/>
</xsd:complexType>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.integration.config;
package org.springframework.integration.channel.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -33,6 +33,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.bus.SubscriptionManager;
import org.springframework.integration.channel.DispatcherPolicy;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.config.TestChannelInterceptor;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageDeliveryException;

View File

@@ -0,0 +1,61 @@
/*
* 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.channel.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.dispatcher.SynchronousChannel;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.StringMessage;
/**
* @author Mark Fisher
*/
public class DirectChannelParserTests {
@Test
public void testChannelWithoutSource() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"directChannelParserTests.xml", DirectChannelParserTests.class);
SynchronousChannel channel = (SynchronousChannel) context.getBean("channelWithoutSource");
assertNull(channel.receive());
Message<?> message = new StringMessage("test");
assertTrue(channel.send(message));
Message<?> reply = channel.receive();
assertNotNull(reply);
assertEquals(message, reply);
}
@Test
public void testChannelWithSource() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"directChannelParserTests.xml", DirectChannelParserTests.class);
SynchronousChannel channel = (SynchronousChannel) context.getBean("channelWithSource");
assertFalse(channel.send(new StringMessage("test")));
Message<?> reply = channel.receive();
assertNotNull(reply);
assertEquals("foo", reply.getPayload());
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.channel.config;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.channel.RendezvousChannel;
/**
* @author Mark Fisher
*/
public class RendezvousChannelParserTests {
@Test
public void testRendezvous() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"rendezvousChannelParserTests.xml", RendezvousChannelParserTests.class);
MessageChannel channel = (MessageChannel) context.getBean("channel");
assertEquals(RendezvousChannel.class, channel.getClass());
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.channel.config;
/**
* @author Mark Fisher
*/
public class TestSourceBean {
private final String text;
public TestSourceBean(String text) {
this.text = text;
}
public String getText() {
return this.text;
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-core-1.0.xsd">
<direct-channel id="channelWithoutSource"/>
<direct-channel id="channelWithSource" source="source"/>
<source-adapter id="source" ref="testSourceBean" method="getText"/>
<beans:bean id="testSourceBean" class="org.springframework.integration.channel.config.TestSourceBean">
<beans:constructor-arg value="foo"/>
</beans:bean>
</beans:beans>

View File

@@ -9,9 +9,9 @@
<priority-channel id="priorityChannelWithDefaultComparator"/>
<priority-channel id="priorityChannelWithCustomComparator" comparator-ref="payloadComparator"/>
<priority-channel id="priorityChannelWithCustomComparator" comparator="payloadComparator"/>
<priority-channel id="integerOnlyPriorityChannel" datatype="java.lang.Integer" comparator-ref="payloadComparator"/>
<priority-channel id="integerOnlyPriorityChannel" datatype="java.lang.Integer" comparator="payloadComparator"/>
<beans:bean id="payloadComparator"
class="org.springframework.integration.channel.MessagePayloadTestComparator"/>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-core-1.0.xsd">
<rendezvous-channel id="channel"/>
</beans:beans>

View File

@@ -16,7 +16,6 @@
reaper-interval="135" tracked-correlation-id-capacity="99"
timeout="42" />
<aggregator id="aggregatorWithReferenceAndMethod" ref="adderBean"
method="add" default-reply-channel="replyChannel" />

View File

@@ -7,9 +7,6 @@
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-core-1.0.xsd">
<aggregator id="aggregatorWithPojoCompletionStrategy" completion-strategy="completionStrategy"
ref="adderBean" method="add" default-reply-channel="replyChannel">
<completion-strategy ref="pojoCompletionStrategy"