Platinum Partner
architects,enterprise-integration,xml,integration,tips and tricks,mule esb

Validating XML message in Mule 3

mulesoft-logo
A common use case Mule ESB flows is validating if an XML document is valid against a corresponding XSD. It can be quite annoying to find out you have spent a lot of time fixing an issue when it was actually caused by another system that was supplying invalid XML, especially if a project is in the development/test stage.

The ‘standard’ way to do this in Mule is by using the schema-validation-filter, usually in combination with the message-filter. For example, the following configuration will send all non-valid messages to the ‘process-invalid-xml’ flow:

<message-filter onUnaccepted="process-invalid-xml" throwOnUnaccepted="true">
    <xml:schema-validation-filter .../>
</message-filter>

In the ‘process-invalid-xml’ flow you can then create a response message (if applicable) to tell the sender of the original message that it contains invalid XML. This was the case in my situation and I also wanted to show the user what was wrong in the XML. To do this, I created my own SAX ErrorHandler and supplied that to the schema-validation-filter like this:

<spring:bean id="xmlErrorHandler" class="net.pascalalma.xml.handlers.MySaxErrorHandler" />
 <message-filter onUnaccepted="invalidRelationXmlFlow" throwOnUnaccepted="false">
     <mulexml:schema-validation-filter errorHandler-ref="xmlErrorHandler"  schemaLocations="xsd/my-schema.xsd"/>
</message-filter>

However, no matter what I did my error handler wasn’t being called by the filter. After some debugging I only got it working by extending the original schema-validation-filter and set the error-handler on the created Validator like this:

public class MySchemaValidationFilter extends SchemaValidationFilter
{
  static Logger logger = Logger.getLogger(MySchemaValidationFilter.class);
 
  public Validator createValidator() throws SAXException
  {
    Validator validator = super.createValidator();
    validator.setErrorHandler(getErrorHandler());
    return validator;
  }
}

I am not sure if this is a bug in the original schema-validation-filter or if there is another way to make use of the errorHandler, but this was the only way for me to get it working.

I use my version of the filter with the following configuration:

<custom-filter class="net.pascalalma.xml.filters.MySchemaValidationFilter" >
  <spring:property name="schemaLocations" value="xsd/my-schema.xsd"/>
  <spring:property name="returnResult" value="false"/>
  <spring:property name="errorHandler" ref="xmlErrorHandler"/>
</custom-filter>

This way I can collect all errors in my error handler and put them with a outbound property on the message and return them to the calling client.

Here is the source code of my error handler:

public class MyErrorHandler implements ErrorHandler {
 
    static Logger logger = Logger.getLogger(MyErrorHandler.class);
 
    public List<String> getErrors() {
        return errors;
    }
    public void setErrors(List<String> errors) {
        this.errors = errors;
    }
    private List<String> errors = new ArrayList<String>();
    @Override
    public void warning(SAXParseException e) throws SAXException {
        logger.warn(e.getLineNumber() + "/" + e.getColumnNumber() + ": " + e.getMessage());
    }
    @Override
    public void fatalError(SAXParseException e) throws SAXException {
        logger.debug("fatalError occurred: " + e.toString());
        errors.add(e.getLineNumber() +"/" + e.getColumnNumber() + ": " + e.getMessage());
    }
    @Override
    public void error(SAXParseException e) throws SAXException {
        logger.debug("error occurred: " + e.toString());
        errors.add(e.getLineNumber() +"/" + e.getColumnNumber() + ": " + e.getMessage());
    }
}



Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}