Andriy Kryvtsun
Andriy Kryvtsun

Reputation: 3344

Switch off DTD loading and validation in XmlValidatingMessageSelector

How it's possible to prevent DTD loading (and validation) in XmlValidatingMessageSelector if !DOCTYPE section is present in incoming XML in Spring Integration?

UPDATE

Actually, in our SI config we have this filter config

@Bean
public MessageSelector cageXmlValidator() {
    XmlValidatingMessageSelector selector = new XmlValidatingMessageSelector(
            new ClassPathResource("/CageMessage.xsd"),
            XmlValidatingMessageSelector.SchemaType.XML_SCHEMA);
    selector.setThrowExceptionOnRejection(true);
    return selector;
}    

and use it in our flow in the such way

@Bean
public IntegrationFlow processorFlow(
        ...
        MessageSelector cageXmlValidator,
        Unmarshaller cageXmlUnmarshaller,
        ...) {
    return IntegrationFlows
            .from(cageInputChannel())
            ...
            .filter(cageXmlValidator)
            .transform(new UnmarshallingTransformer(cageXmlUnmarshaller))
            ... 
            .channel(cageOutputChannel())
            .get();
}

I tried also this XmlValidatingMessageSelector definition but it doesn't work

@Bean
public MessageSelector cageXmlValidator() throws Exception {
    XmlValidatingMessageSelector selector = new XmlValidatingMessageSelector(
            new ClassPathResource("/CageMessage.xsd"),
            XmlValidatingMessageSelector.SchemaType.XML_SCHEMA);

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
    factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
    selector.setConverter(new DefaultXmlPayloadConverter(factory));

    selector.setThrowExceptionOnRejection(true);
    return selector;
}

Upvotes: 1

Views: 94

Answers (1)

Artem Bilan
Artem Bilan

Reputation: 121542

I have a test-case like:

@Test
public void testValidMessage() throws Exception {
    Document doc = XmlTestUtil.getDocumentForString("<!DOCTYPE greeting SYSTEM \"greeting.dtd\"><greeting>hello</greeting>");
    GenericMessage<Document> docMessage = new GenericMessage<Document>(doc);
    PollableChannel validChannel = ac.getBean("validOutputChannel", PollableChannel.class);
    MessageChannel inputChannel = ac.getBean("inputChannelA", MessageChannel.class);
    inputChannel.send(docMessage);
    assertNotNull(validChannel.receive(100));
}

Pay attention to the !DOCTYPE declaration in the MXL snippet.

That XmlTestUtil.getDocumentForString() has this code:

DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
builder.setNamespaceAware(true);
builder.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
builder.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);


return builder.newDocumentBuilder().parse(
        new InputSource(new StringReader(strDoc)));

The latest load-external-dtd feature does exactly what I need.

So, what you need in your application is to consider to build Document for your XML in advance before validation and use those feature for the DocumentBuilderFactory.

UPDATE

The inserting a customized DefaultXmlPayloadConverter with an appropriate DocumentBuilderFactory doesn't have effect for our payload:

validationExceptions = this.xmlValidator.validate(this.converter.convertToSource(message.getPayload()));

Where that convertToSource() looks like:

public Source convertToSource(Object object) {
    Source source = null;
    if (object instanceof Source) {
        source = (Source) object;
    }
    else if (object instanceof Document) {
        source = new DOMSource((Document) object);
    }
    else if (object instanceof String) {
        source = new StringSource((String) object);
    }
    else {
        throw new MessagingException("unsupported payload type [" + object.getClass().getName() + "]");
    }
    return source;
}

As you see nobody calls that DocumentBuilderFactory.

What I suggest you to have a .transform() upfront .filter(cageXmlValidator) to transform your payload to the Document object with the customized DocumentBuilderFactory.

Seeing the fact that we can't affect internals of the javax.xml.validation.Validator, I think that would be good compromise to call convertToDocument() from the validator and wrap it into DOMSource. That way, indeed, your DefaultXmlPayloadConverter would have an effect.

Feel free to raise a JIRA or even consider contribution.

Thank you for pointing that out!

Upvotes: 1

Related Questions