Validating XML message in Mule 3

mulesoft-logo
A quite common use case in a flow in the Mule ESB is validating if an XML document is valid against a corresponding XSD. Especially if a project is in the development/test stage 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 which was supplying invalid XML.
The ‘standard’ way to do this in Mule is by using the schema-validation-filter and especially 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;
  }
}

Update 31-01-2014: Please note that this implementation is NOT thread safe since the errorHandler is defined at class level. To make this filter thread-safe I have also overwritten the ‘accept()’ method so I get an ‘errorHandler’ instance per ‘validator’ instance.

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());
    }
}
Advertisement

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in Mule3, XML/ XSD/ XSLT and tagged , . Bookmark the permalink.

2 Responses to Validating XML message in Mule 3

  1. Deep says:

    How are you setting the validation errors into the message?
    Can u please explain that

    • Pascal Alma says:

      Hi Deep,

      Here is ‘an’ implementation for that. I override the ‘accept’ method in the ValidationFilter like this:

      /**
           * Accepts the message if schema validation passes.
           *
           * @param message The message.
           * @return Whether the message passes schema validation.
           */
          public boolean accept(MuleMessage message) {
              // Check if validations should really be executed.
              // Can be quite time consuming so should be turned of
              // in a production environment.
              if (!isPerformValidation()) {
                  return true;
              }
              // Set the default for returnResult to false
              setReturnResult(defaultReturnResult);
      
              boolean result = super.accept(message);
      
              if (getErrorHandler()== null || getErrorHandler() instanceof MySaxErrorHandler) {
                  List errors = ((MySaxErrorHandler) getErrorHandler()).getErrors();
                  message.setOutboundProperty(propertyName, StringUtils.join(errors, delimiter));
                  result = (errors == null || errors.size() == 0);
              } else {
                  if (! result ) {
                      // This can happen if the filter fails before the Validator is created for instance because the
                      // input xml was empty or the supplied XSD doesn't exists
                      message.setOutboundProperty(propertyName, "Error initializing the filter probably because of empty XML doc or non-existing XSD (see logging for detail).");
                  }
              }
              return result;
          }
      

      I hope this helps!
      If you have more questions let me know.

Comments are closed.