INT-2218 - Chain Parser Validation Improvements

Components within Chain: Add parser validation

For reference see: https://jira.springsource.org/browse/INT-2218

INT-2218 - Code review changes

INT-2218 - Fix HttpOutboundGatewayParserTests

INT-2218 - Remove input-channel validation

Was already covered by PR #592
This commit is contained in:
Gunnar Hillert
2012-09-12 10:07:18 -04:00
committed by Gary Russell
parent c9f73a8a49
commit 7055845424
10 changed files with 455 additions and 7 deletions

View File

@@ -13,10 +13,13 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -24,6 +27,8 @@ import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the <chain> element.
@@ -32,14 +37,14 @@ import org.springframework.integration.handler.MessageHandlerChain;
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gunnar Hillert
*/
public class ChainParser extends AbstractConsumerEndpointParser {
@Override
@SuppressWarnings("unchecked")
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageHandlerChain.class);
ManagedList handlerList = new ManagedList();
ManagedList<BeanMetadataElement> handlerList = new ManagedList<BeanMetadataElement>();
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
@@ -63,12 +68,38 @@ public class ChainParser extends AbstractConsumerEndpointParser {
return builder;
}
private void validateChild(Element element, ParserContext parserContext) {
final Object source = parserContext.extractSource(element);
final String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
if (StringUtils.hasText(order)) {
parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
"an 'order' attribute when used within a chain.", source);
}
final List<Element> pollerChildElements = DomUtils
.getChildElementsByTagName(element, "poller");
if (!pollerChildElements.isEmpty()) {
parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
"a 'poller' sub-element when used within a chain.", source);
}
}
private BeanDefinitionHolder parseChild(Element element, ParserContext parserContext, BeanDefinition parentDefinition) {
BeanDefinitionHolder holder = null;
if ("bean".equals(element.getLocalName())) {
holder = parserContext.getDelegate().parseBeanDefinitionElement(element, parentDefinition);
}
else {
this.validateChild(element, parserContext);
BeanDefinition beanDefinition = parserContext.getDelegate().parseCustomElement(element, parentDefinition);
if (beanDefinition == null) {
parserContext.getReaderContext().error("child BeanDefinition must not be null", element);

View File

@@ -20,8 +20,10 @@ import java.util.Properties;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
import org.springframework.context.ApplicationContext;
@@ -208,6 +210,23 @@ public class ChainElementsFailureTests {
}
}
@Test
public void chainResequencerPoller() throws Exception {
try {
this.bootStrap("resequencer-poller");
fail("Expected a XmlBeanDefinitionStoreException to be thrown.");
}
catch (BeanDefinitionParsingException e) {
final String expectedMessage = "Configuration problem: " +
"'int:resequencer' must not define a 'poller' sub-element " +
"when used within a chain.";
final String actualMessage = e.getMessage();
assertTrue("Error message did not start with '" + expectedMessage +
"' but instead returned: '" + actualMessage + "'", actualMessage.startsWith(expectedMessage));
}
}
private ApplicationContext bootStrap(String configProperty) throws Exception {
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
pfb.setLocation(new ClassPathResource("org/springframework/integration/config/xml/chain-elements-config.properties"));

View File

@@ -75,3 +75,10 @@ resequencer=\
<int:chain input-channel="input"> \
<int:resequencer input-channel="fail"/> \
</int:chain>
resequencer-poller=\
<int:chain input-channel="input"> \
<int:resequencer> \
<int:poller fixed-rate="5000" max-messages-per-poll="10" />\
</int:resequencer> \
</int:chain>