INT-677, removed AbstractInnerDefinitionAwareEndpointParser, re-factored AbstractConsumerEndpointParser by adding helper method parseInnerHandlerDefinition(..), re-factored SplitterParser and TransformerParser, added inner handler support for 'sa' and 'router' elements

This commit is contained in:
Oleg Zhurakousky
2009-06-25 01:48:24 +00:00
parent 4d3c9d32fc
commit 0a6ed75b3a
9 changed files with 258 additions and 91 deletions

View File

@@ -16,21 +16,28 @@
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.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
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.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Base class parser for elements that create Message Endpoints.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinitionParser {
@@ -101,5 +108,25 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
return builder.getBeanDefinition();
}
/**
*
* @return
*/
protected BeanDefinition parseInnerHandlerDefinition(Element element, ParserContext parserContext){
// parses out inner bean definition for concrete implementation if defined
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "bean");
BeanDefinition innerDefinition = null;
if (childElements != null && childElements.size() == 1){
Element beanElement = childElements.get(0);
BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
innerDefinition = delegate.parseBeanDefinitionElement(beanElement).getBeanDefinition();
}
String ref = element.getAttribute(REF_ATTRIBUTE);
Assert.isTrue(!(StringUtils.hasText(ref) && innerDefinition != null), "Ambiguous definition. Inner bean " +
(innerDefinition == null ? innerDefinition : innerDefinition.getBeanClassName()) + " declaration and \"ref\" " + ref +
" are not allowed together.");
return innerDefinition;
}
}

View File

@@ -1,68 +0,0 @@
/*
* 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.List;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Abstract Parser for any consumers that require capabilities to define
* its handler implementation as inner bean.<br>
* For example:<br>
* <pre>
* &lt;transformer id="testTransformer" input-channel="inChannel" output-channel="outChannel"&gt;
* &lt;beans:bean class="org.bar.TestTransformer"/&gt;
* &lt;/transformer&gt;
* </pre>
*
* @author Oleg Zhurakousky
* @since 1.0.3
*/
public abstract class AbstractInnerDefinitionAwareEndpointParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
// parses out inner bean definition for concrete implementation if defined
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "bean");
BeanDefinition innerDefinition = null;
if (childElements != null && childElements.size() == 1){
Element beanElement = childElements.get(0);
BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
innerDefinition = delegate.parseBeanDefinitionElement(beanElement).getBeanDefinition();
}
String ref = element.getAttribute(REF_ATTRIBUTE);
Assert.isTrue(!(StringUtils.hasText(ref) && innerDefinition != null), "Ambiguous definition. Inner bean " +
(innerDefinition == null ? innerDefinition : innerDefinition.getBeanClassName()) + " declaration and \"ref\" " + ref +
" are not allowed together.");
return this.parseEndpoint(element, parserContext, innerDefinition);
}
/**
*
* @param element
* @param parserContext
* @param innerDefinition
* @return
*/
protected abstract BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition);
}

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.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.ParserContext;
@@ -33,11 +34,17 @@ public class DefaultRouterParser extends AbstractRouterParser {
@Override
protected void parseRouter(Element element, BeanDefinitionBuilder builder, ParserContext parserContext) {
String ref = element.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(ref)) {
parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required.", element);
BeanDefinition innerDefinition = this.parseInnerHandlerDefinition(element, parserContext);
if (innerDefinition != null){
builder.addPropertyValue("targetObject", innerDefinition);
} else {
String ref = element.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(ref)) {
parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required.", element);
}
builder.addPropertyReference("targetObject", ref);
}
builder.addPropertyReference("targetObject", ref);
if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) {
String method = element.getAttribute(METHOD_ATTRIBUTE);
builder.addPropertyValue("targetMethodName", method);

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;
import org.springframework.util.StringUtils;
@@ -26,19 +27,27 @@ import org.springframework.util.StringUtils;
* Parser for the &lt;service-activator&gt; element.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class ServiceActivatorParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinition innerHandlerDefinition = this.parseInnerHandlerDefinition(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".handler.ServiceActivatingHandler");
String ref = element.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(ref)) {
parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required for element "
+ IntegrationNamespaceUtils.createElementDescription(element) + ".", element);
if (innerHandlerDefinition != null){
builder.addConstructorArgValue(innerHandlerDefinition);
} else {
String ref = element.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(ref)) {
parserContext.getReaderContext().error("The '" + REF_ATTRIBUTE + "' attribute is required for element "
+ IntegrationNamespaceUtils.createElementDescription(element) + ".", element);
}
builder.addConstructorArgReference(ref);
}
builder.addConstructorArgReference(ref);
if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) {
String method = element.getAttribute(METHOD_ATTRIBUTE);
builder.getRawBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue(method, "java.lang.String");

View File

@@ -29,10 +29,11 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class SplitterParser extends AbstractInnerDefinitionAwareEndpointParser {
public class SplitterParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition) {
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinition innerDefinition = this.parseInnerHandlerDefinition(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.SplitterFactoryBean");
if (innerDefinition != null){

View File

@@ -28,10 +28,11 @@ import org.w3c.dom.Element;
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class TransformerParser extends AbstractInnerDefinitionAwareEndpointParser {
public class TransformerParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseEndpoint(Element element, ParserContext parserContext, BeanDefinition innerDefinition) {
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinition innerDefinition = this.parseInnerHandlerDefinition(element, parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".config.TransformerFactoryBean");

View File

@@ -354,7 +354,7 @@
default="true" />
</xsd:complexType>
<xsd:element name="service-activator" type="handlerEndpointType">
<xsd:element name="service-activator" type="innerEndpointDefinitionAware">
<xsd:annotation>
<xsd:documentation>
Defines an endpoint for exposing any bean
@@ -907,9 +907,13 @@
Spring-managed object as specified by the "ref" and "method" attributes.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="routerType">
<xsd:choice>
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -923,6 +927,7 @@
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="routerType">
@@ -1181,13 +1186,13 @@
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="innerEndpointDefinitionAware">
<xsd:complexContent>
<xsd:extension base="handlerEndpointType">
<xsd:choice>
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:choice>
</xsd:extension>
<xsd:complexType name="innerEndpointDefinitionAware">
<xsd:complexContent>
<xsd:extension base="handlerEndpointType">
<xsd:choice>
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:choice>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:complexType>
</xsd:schema>

View File

@@ -44,7 +44,7 @@ import org.springframework.util.StringUtils;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class InnerDefinitionAwareEndpointParserTests {
public class InnerDefinitionHandlerAwareEndpointParserTests {
@Autowired
private Properties testConfigurations;
@@ -90,6 +90,46 @@ public class InnerDefinitionAwareEndpointParserTests {
reader.loadBeanDefinitions(new InputStreamResource(stream));
}
@Test
public void testInnerRouterDefinitionSuccess(){
String configProperty = testConfigurations.getProperty("router-inner-success");
this.testRouterDefinitionSuccess(configProperty);
}
@Test
public void testRefRouterDefinitionSuccess(){
String configProperty = testConfigurations.getProperty("router-ref-success");
this.testRouterDefinitionSuccess(configProperty);
}
@Test(expected=BeanDefinitionStoreException.class)
public void testInnerRouterDefinitionFailureRefAndInner(){
String xmlConfig = testConfigurations.getProperty("router-failure-refAndBean");
ByteArrayInputStream stream = new ByteArrayInputStream(xmlConfig.getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
reader.loadBeanDefinitions(new InputStreamResource(stream));
}
@Test
public void testInnerSADefinitionSuccess(){
String configProperty = testConfigurations.getProperty("sa-inner-success");
this.testSADefinitionSuccess(configProperty);
}
@Test
public void testRefSADefinitionSuccess(){
String configProperty = testConfigurations.getProperty("sa-ref-success");
this.testSADefinitionSuccess(configProperty);
}
@Test(expected=BeanDefinitionStoreException.class)
public void testInnerSADefinitionFailureRefAndInner(){
String xmlConfig = testConfigurations.getProperty("sa-failure-refAndBean");
ByteArrayInputStream stream = new ByteArrayInputStream(xmlConfig.getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
reader.loadBeanDefinitions(new InputStreamResource(stream));
}
private void testSplitterDefinitionSuccess(String configProperty){
ByteArrayInputStream stream = new ByteArrayInputStream(configProperty.getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
@@ -124,6 +164,41 @@ public class InnerDefinitionAwareEndpointParserTests {
String payload = (String) outChannel.receive().getPayload();
Assert.assertTrue(payload.equals("One,Two"));
}
private void testRouterDefinitionSuccess(String configProperty){
ByteArrayInputStream stream = new ByteArrayInputStream(configProperty.getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
reader.loadBeanDefinitions(new InputStreamResource(stream));
EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testRouter");
Assert.assertNotNull(splitter);
MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload("1");
Message inMessage = inChannelMessageBuilder.build();
DirectChannel inChannel = (DirectChannel) ac.getBean("inChannel");
inChannel.send(inMessage);
PollableChannel channel1 = (PollableChannel) ac.getBean("channel1");
Assert.assertTrue(channel1.receive().getPayload().equals("1"));
inChannelMessageBuilder = MessageBuilder.withPayload("2");
inMessage = inChannelMessageBuilder.build();
inChannel.send(inMessage);
PollableChannel channel2 = (PollableChannel) ac.getBean("channel2");
Assert.assertTrue(channel2.receive().getPayload().equals("2"));
}
private void testSADefinitionSuccess(String configProperty){
ByteArrayInputStream stream = new ByteArrayInputStream(configProperty.getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
reader.loadBeanDefinitions(new InputStreamResource(stream));
EventDrivenConsumer splitter = (EventDrivenConsumer) ac.getBean("testServiceActivator");
Assert.assertNotNull(splitter);
MessageBuilder inChannelMessageBuilder = MessageBuilder.withPayload("1");
Message inMessage = inChannelMessageBuilder.build();
DirectChannel inChannel = (DirectChannel) ac.getBean("inChannel");
inChannel.send(inMessage);
PollableChannel channel1 = (PollableChannel) ac.getBean("outChannel");
Assert.assertTrue(channel1.receive().getPayload().equals("1"));
}
public static class TestSplitter{
public Collection split(String[] payload){
@@ -136,5 +211,16 @@ public class InnerDefinitionAwareEndpointParserTests {
return StringUtils.arrayToDelimitedString(payload, ",");
}
}
public static class TestRouter{
public String route(String value) {
return (value.equals("1")) ? "channel1" : "channel2";
}
}
public static class TestServiceActivator{
public String foo(String value) {
return value;
}
}
}

View File

@@ -93,4 +93,103 @@ transformer-failure-refAndBean=\
<beans:bean class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestTransformer" /> \
</splitter> \
<beans:bean id="testTransformerBean" class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestTransformer" /> \
</beans:beans>
router-inner-success=\
<?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-1.0.xsd"> \
<channel id="inChannel"/> \
<channel id="channel1"> \
<queue capacity="1" /> \
</channel> \
<channel id="channel2"> \
<queue capacity="1" /> \
</channel> \
<router id="testRouter" input-channel="inChannel" method="route"> \
<beans:bean class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestRouter" /> \
</router> \
</beans:beans>
router-ref-success=\
<?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-1.0.xsd"> \
<channel id="inChannel"/> \
<channel id="channel1"> \
<queue capacity="1" /> \
</channel> \
<channel id="channel2"> \
<queue capacity="1" /> \
</channel> \
<router id="testRouter" ref="testRouterBean" input-channel="inChannel" method="route"/> \
<beans:bean id="testRouterBean" class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestRouter" /> \
</beans:beans>
router-failure-refAndBean=\
<?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-1.0.xsd"> \
<channel id="inChannel"/> \
<router id="testRouter" ref="testRouterBean" input-channel="inChannel" method="route"> \
<beans:bean class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestRouter" /> \
<router/> \
<beans:bean id="testRouterBean" class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestRouter" /> \
</beans:beans>
sa-inner-success=\
<?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-1.0.xsd"> \
<channel id="inChannel"/> \
<channel id="outChannel"> \
<queue capacity="1" /> \
</channel> \
<service-activator id="testServiceActivator" input-channel="inChannel" output-channel = "outChannel" method="foo"> \
<beans:bean class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestServiceActivator" /> \
</service-activator> \
</beans:beans>
sa-ref-success=\
<?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-1.0.xsd"> \
<channel id="inChannel"/> \
<channel id="outChannel"> \
<queue capacity="1" /> \
</channel> \
<service-activator id="testServiceActivator" ref="testServiceActivatorBean" input-channel="inChannel" output-channel = "outChannel" method="foo"/> \
<beans:bean id="testServiceActivatorBean" class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestServiceActivator" /> \
</beans:beans>
sa-failure-refAndBean=\
<?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-1.0.xsd"> \
<channel id="inChannel"/> \
<channel id="outChannel"> \
<queue capacity="1" /> \
</channel> \
<service-activator id="testServiceActivator" ref="testServiceActivatorBean" input-channel="inChannel" output-channel = "outChannel" method="foo"> \
<beans:bean class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestServiceActivator" /> \
</service-activator> \
<beans:bean id="testServiceActivatorBean" class="org.springframework.integration.config.xml.InnerDefinitionAwareEndpointParserTests$TestServiceActivator" /> \
</beans:beans>