INT-1515 initial round of refactoring to turn validating-router into validating-filter

This commit is contained in:
Oleg Zhurakousky
2010-10-14 05:56:19 -04:00
parent f5fe76d715
commit a389fa9795
10 changed files with 223 additions and 345 deletions

View File

@@ -34,7 +34,7 @@ public class IntegrationXmlNamespaceHandler extends AbstractIntegrationNamespace
registerBeanDefinitionParser("xpath-selector", new XPathSelectorParser());
registerBeanDefinitionParser("xpath-expression", new XPathExpressionParser());
registerBeanDefinitionParser("xpath-splitter", new XPathMessageSplitterParser());
registerBeanDefinitionParser("validating-router", new XmlPayloadValidatingRouterParser());
registerBeanDefinitionParser("validating-filter", new XmlPayloadValidatingFilterParser());
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.xml.config;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.w3c.dom.Element;
/**
* @author Jonas Partner
* @author Oleg Zhurakousky
*/
public class XmlPayloadValidatingFilterParser extends AbstractConsumerEndpointParser {
private static String SELECTOR =
"org.springframework.integration.xml.selector.SchemaValidatingMessageSelector";
private static String FILTER =
"org.springframework.integration.config.FilterFactoryBean";
@Override
protected boolean shouldGenerateId() {
return true;
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.genericBeanDefinition(FILTER);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(filterBuilder, element, "discard-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(filterBuilder, element, "throw-exception-on-rejection");
BeanDefinitionBuilder selectorBuilder = BeanDefinitionBuilder.genericBeanDefinition(SELECTOR);
selectorBuilder.addConstructorArgValue(element.getAttribute("schema-location"));
selectorBuilder.addPropertyValue("schemaType", element.getAttribute("schema-type"));
filterBuilder.addPropertyValue("targetObject", selectorBuilder.getBeanDefinition());
return filterBuilder;
}
}

View File

@@ -1,101 +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.xml.config;
import javax.xml.XMLConstants;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.xml.router.SchemaValidator;
import org.springframework.integration.xml.router.XmlPayloadValidatingRouter;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author Jonas Partner
*/
public class XmlPayloadValidatingRouterParser extends
AbstractConsumerEndpointParser {
@Override
protected boolean shouldGenerateId() {
return false;
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected BeanDefinitionBuilder parseHandler(Element element,
ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition();
builder.getBeanDefinition().setBeanClass(
XmlPayloadValidatingRouter.class);
String channelResolver = element.getAttribute("channel-resolver");
String validChannelName = element.getAttribute("valid-channel");
String invalidChannelName = element.getAttribute("invalid-channel");
String schemaType = element.getAttribute("schema-type");
String schemaLocation = element.getAttribute("schema-location");
Assert.state(schemaType.equals("xml-schema")
|| schemaType.equals("relax-ng"), "Unrecognised schema type "
+ schemaType);
Assert.state(StringUtils.hasText(invalidChannelName)
&& StringUtils.hasText(validChannelName),
"valid-channel and invalid-channel must both be specified");
builder.addConstructorArgValue(validChannelName);
builder.addConstructorArgValue(invalidChannelName);
BeanDefinition validatorBeanDefinition;
if (schemaType.equals("xml-schema")) {
validatorBeanDefinition = createValidator(XMLConstants.W3C_XML_SCHEMA_NS_URI, schemaLocation);
} else {
validatorBeanDefinition = createValidator(XMLConstants.RELAXNG_NS_URI, schemaLocation);
}
builder.addConstructorArgValue(validatorBeanDefinition);
if (StringUtils.hasText(channelResolver)) {
builder.addPropertyReference("channelResolver", channelResolver);
}
return builder;
}
protected BeanDefinition createValidator(String schemaType, String schemaLocation){
BeanDefinitionBuilder xmlValidator = BeanDefinitionBuilder
.genericBeanDefinition();
xmlValidator.getBeanDefinition().setBeanClass(SchemaValidator.class);
xmlValidator.addConstructorArgValue(schemaLocation);
xmlValidator.addConstructorArgValue(schemaType);
return xmlValidator.getBeanDefinition();
}
}

View File

@@ -1,61 +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.xml.router;
import org.springframework.integration.Message;
import org.springframework.integration.router.AbstractSingleChannelNameRouter;
import org.springframework.integration.xml.DefaultXmlPayloadConverter;
import org.springframework.integration.xml.XmlPayloadConverter;
public class XmlPayloadValidatingRouter extends AbstractSingleChannelNameRouter{
private final String validMessageChannelName;
private final String invalidMessageChannelName;
private final XmlValidator xmlValidator;
private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
public XmlPayloadValidatingRouter(String validMessageChannelName,
String invalidMessageChannelName, XmlValidator xmlValidator) {
super();
this.validMessageChannelName = validMessageChannelName;
this.invalidMessageChannelName = invalidMessageChannelName;
this.xmlValidator = xmlValidator;
}
/**
* Converter used to convert payloads prior to validation
*
* @param converter
*/
public void setConverter(XmlPayloadConverter converter) {
this.converter = converter;
}
@Override
protected String determineTargetChannelName(Message<?> message) {
return xmlValidator.isValid(converter.convertToSource(message.getPayload())) ? validMessageChannelName : invalidMessageChannelName;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.xml.selector;
import org.springframework.core.io.Resource;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.xml.DefaultXmlPayloadConverter;
import org.springframework.integration.xml.XmlPayloadConverter;
import org.springframework.util.Assert;
import org.springframework.xml.validation.XmlValidator;
import org.springframework.xml.validation.XmlValidatorFactory;
import org.xml.sax.SAXParseException;
/**
*
* @author Oleg Zhurakousky
* @since 2.0
*
*/
public class SchemaValidatingMessageSelector implements MessageSelector{
private final XmlValidator xmlValidator;
private volatile String schemaType = XmlValidatorFactory.SCHEMA_W3C_XML;
private volatile XmlPayloadConverter converter = new DefaultXmlPayloadConverter();
public SchemaValidatingMessageSelector(Resource schema) throws Exception{
Assert.notNull(schema, "You must provide XML schema location to perform validation");
this.xmlValidator = XmlValidatorFactory.createValidator(schema, schemaType);
}
/**
* Converter used to convert payloads prior to validation
*
* @param converter
*/
public void setConverter(XmlPayloadConverter converter) {
this.converter = converter;
}
public void setSchemaType(String schemaType) {
this.schemaType = schemaType;
}
@Override
public boolean accept(Message<?> message) {
// TODO Need to figure out how the exceptions could be propagated since the return from this method is true/false
// and 'throw-exception-on-rejection'is actually set on the filter
try {
SAXParseException[] validationExceptions = xmlValidator.validate(converter.convertToSource(message.getPayload()));
return validationExceptions.length == 0 ? true : false;
} catch (Exception e) {
e.printStackTrace();
throw new MessageHandlingException(message, e);
}
}
}

View File

@@ -510,11 +510,11 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="validating-router">
<xsd:element name="validating-filter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines a validating router.
Defines a validating filter.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
@@ -530,17 +530,8 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel-resolver" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.core.ChannelResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="valid-channel" type="xsd:string" use="required" />
<xsd:attribute name="invalid-channel" type="xsd:string" use="required" />
<xsd:attribute name="output-channel" type="xsd:string" use="required" />
<xsd:attribute name="discard-channel" type="xsd:string" use="required" />
<xsd:attribute name="schema-location" use="required" />
<xsd:attribute name="schema-type" default="xml-schema">
<xsd:simpleType>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-xml="http://www.springframework.org/schema/integration/xml"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml-2.0.xsd">
<int-xml:validating-filter id="filter"
input-channel="inputChannel"
output-channel="validOutputChannel"
discard-channel="invalidOutputChannel"
schema-location="org/springframework/integration/xml/config/validationTestsSchema.xsd"/>
<int:channel id="validOutputChannel">
<int:queue/>
</int:channel>
<int:channel id="invalidOutputChannel">
<int:queue/>
</int:channel>
</beans>

View File

@@ -0,0 +1,62 @@
/*
* 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.xml.config;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.xml.util.XmlTestUtil;
import org.springframework.test.context.ContextConfiguration;
import org.w3c.dom.Document;
/**
* @author Jonas Partner
* @author Oleg Zhurakousky
*/
@ContextConfiguration
public class XmlPayloadValidatingFilterParserTests {
@Test
public void testValidMessage() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("XmlPayloadValidatingFilterParserTests-context.xml", this.getClass());
Document doc = XmlTestUtil.getDocumentForString("<greeting>hello</greeting>");
GenericMessage<Document> docMessage = new GenericMessage<Document>(doc);
PollableChannel validChannel = ac.getBean("validOutputChannel", PollableChannel.class);
MessageChannel inputChannel = ac.getBean("inputChannel", MessageChannel.class);
inputChannel.send(docMessage);
assertNotNull(validChannel.receive(100));
}
@Test
public void testInvalidMessage() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("XmlPayloadValidatingFilterParserTests-context.xml", this.getClass());
Document doc = XmlTestUtil.getDocumentForString("<greeting><other/></greeting>");
GenericMessage<Document> docMessage = new GenericMessage<Document>(doc);
PollableChannel validChannel = ac.getBean("validOutputChannel", PollableChannel.class);
PollableChannel invalidChannel = ac.getBean("invalidOutputChannel", PollableChannel.class);
MessageChannel inputChannel = ac.getBean("inputChannel", MessageChannel.class);
inputChannel.send(docMessage);
assertNotNull(invalidChannel.receive(100));
assertNull(validChannel.receive(100));
}
}

View File

@@ -1,88 +0,0 @@
/*
* 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.xml.config;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.xml.util.XmlTestUtil;
import org.springframework.test.context.ContextConfiguration;
import org.w3c.dom.Document;
/**
* @author Jonas Partner
*/
@ContextConfiguration
public class XmlPayloadValidatingRouterParserTests {
String channelConfig = "<si:channel id='test-input'/> <si:channel id='validOutputChannel'><si:queue capacity='10'/></si:channel> <si:channel id='invalidOutputChannel'><si:queue capacity='10'/></si:channel>";
@Autowired @Qualifier("test-input")
MessageChannel inputChannel;
@Autowired @Qualifier("validOutputChannel")
QueueChannel validOutputChannel;
@Autowired @Qualifier("invalidOutputChannel")
QueueChannel invalidOutputChannel;
ConfigurableApplicationContext appContext;
public EventDrivenConsumer buildContext(String routerDef){
appContext = TestXmlApplicationContextHelper.getTestAppContext( channelConfig + routerDef);
appContext.getAutowireCapableBeanFactory().autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
EventDrivenConsumer consumer = (EventDrivenConsumer) appContext.getBean("router");
consumer.start();
return consumer;
}
@After
public void tearDown(){
if(appContext != null){
appContext.close();
}
}
@Test
public void testValidMessage() throws Exception {
Document doc = XmlTestUtil.getDocumentForString("<greeting>hello</greeting>");
GenericMessage<Document> docMessage = new GenericMessage<Document>(doc);
buildContext("<si-xml:validating-router id='router' input-channel='test-input' valid-channel='validOutputChannel' invalid-channel='invalidOutputChannel' schema-location='org/springframework/integration/xml/config/validationTestsSchema.xsd' />");
inputChannel.send(docMessage);
assertEquals("Wrong number of messages", 1, validOutputChannel.getQueueSize());
}
@Test
public void testInvalidMessage() throws Exception {
Document doc = XmlTestUtil.getDocumentForString("<greeting><other/></greeting>");
GenericMessage<Document> docMessage = new GenericMessage<Document>(doc);
buildContext("<si-xml:validating-router id='router' input-channel='test-input' valid-channel='validOutputChannel' invalid-channel='invalidOutputChannel' schema-location='org/springframework/integration/xml/config/validationTestsSchema.xsd' />");
inputChannel.send(docMessage);
assertEquals("Wrong number of messages", 1, invalidOutputChannel.getQueueSize());
}
}

View File

@@ -1,81 +0,0 @@
package org.springframework.integration.xml.router;
import static org.junit.Assert.*;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import org.junit.Before;
/*
* 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.
*/
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.support.MessageBuilder;
public class XmlPayloadValidatingRouterTests {
String validChannelName = "VALID";
String invalidChannelName = "INVALID";
Source testSource;
Message<Source> testMessage;
@Before
public void setUp(){
testSource = new SAXSource();
testMessage = MessageBuilder.withPayload(testSource).build();
}
@Test
public void testValidMessage(){
StubValidator validator = new StubValidator(true);
XmlPayloadValidatingRouter router = new XmlPayloadValidatingRouter(validChannelName, invalidChannelName, validator);
String returnedChannelName = router.determineTargetChannelName(testMessage);
assertEquals("Wrong channel name", validChannelName, returnedChannelName);
assertEquals("Source not passed to validator ", testSource, validator.passedIn);
}
@Test
public void testInvalidMessage(){
StubValidator validator = new StubValidator(false);
XmlPayloadValidatingRouter router = new XmlPayloadValidatingRouter(validChannelName, invalidChannelName, validator);
String returnedChannelName = router.determineTargetChannelName(testMessage);
assertEquals("Wrong channel name", invalidChannelName, returnedChannelName);
assertEquals("Source not passed to validator ", testSource, validator.passedIn);
}
static class StubValidator implements XmlValidator {
private final boolean validationResult;
Source passedIn;
public StubValidator(boolean validationResult) {
this.validationResult = validationResult;
}
public boolean isValid(Source source) {
passedIn = source;
return validationResult;
}
}
}