javaspring-wsmtomsaajxop

Is SAAJ compatible with MTOM + XOP?


I´m trying to use MTOM + XOP to return a byte[] in a Spring Web Service but when marshalling result it throws a SAXParseException with message:

cvc-type.3.1.2: Element 'dataHandler' is a simple type, so it must have no element information item [children].

It seems that after writing XOP tag, validator meet with it instead of the byte[] content that it expects and throws a SAXParseException with FATAL level that stops the process.

I´m using:

Thanks in advance


Solution

  • I´ve found two workarounds for my situation:

    1. Not setting the xmlschema to marshaller.

    2. Setting a ValidationEventHandler to marshaller that skips XOP failures.

    This is an example of setting a ValidationEventHandler to marshaller that skips XOP failures:

    Abstract superclass:

    import javax.xml.bind.ValidationEvent;
    import javax.xml.bind.ValidationEventHandler;
    
    abstract class XopAwareValidationEventHandler implements ValidationEventHandler {
    
      private static final String CVC_TYPE_3_1_2 = "cvc-type.3.1.2";
    
      private ValidationEventHandler realHandler;
    
      XopAwareValidationEventHandler(final ValidationEventHandler handler) {
        this.setRealHandler(handler);
      }
    
      @Override
      public boolean handleEvent(final ValidationEvent event) {
        final boolean result = this.getRealHandler().handleEvent(event);
        if (!result) {
          if (event.getMessage() != null && event.getMessage().startsWith(CVC_TYPE_3_1_2)) {
            return this.isXopEvent(event);
          }
        }
        return result;
      }
    
      abstract boolean isXopEvent(ValidationEvent validationEvent);
    
      private ValidationEventHandler getRealHandler() {
        return realHandler;
      }
    
      private void setRealHandler(final ValidationEventHandler realHandler) {
        this.realHandler = realHandler;
      }
    }
    

    Concrete class for unmarshaller:

    import javax.xml.bind.ValidationEvent;
    import javax.xml.bind.ValidationEventHandler;
    
    class XopAwareUnmarshallingValidationEventHandler extends XopAwareValidationEventHandler {
    
      private static final String XOP_INCLUDE = "xop:Include";
    
      XopAwareUnmarshallingValidationEventHandler(final ValidationEventHandler handler) {
        super(handler);
      }
    
      @Override
      boolean isXopEvent(final ValidationEvent validationEvent) {
        final ValidationEventLocator locator = validationEvent.getLocator();
        return locator != null && locator.getNode() != null &&
            locator.getNode().getFirstChild() != null &&
            XOP_INCLUDE.equals(locator.getNode().getFirstChild().getNodeName());
      }
    }
    

    For marshaller I´m searching for the condition that identifies the case, because the ValidationEventLocator only have the Object set and it could be whatever.

    import javax.xml.bind.ValidationEvent;
    import javax.xml.bind.ValidationEventHandler;
    import javax.xml.bind.ValidationEventLocator;
    
    public class XopAwareMarshallingValidationEventHandler extends XopAwareValidationEventHandler {
    
      public XopAwareMarshallingValidationEventHandler(final ValidationEventHandler handler) {
        super(handler);
      }
    
      boolean isXopEvent(final ValidationEvent validationEvent) {
        final ValidationEventLocator locator = validationEvent.getLocator();
        return locator != null && locator.getNode() == null;
      }
    }
    

    org.springframework.oxm.jaxb.Jaxb2Marshaller subclass that activates MTOM and adds both event handlers:

    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    
    import org.springframework.oxm.jaxb.Jaxb2Marshaller;
    
    import XopAwareMarshallingValidationEventHandler;
    import XopAwareUnmarshallingValidationEventHandler;
    
    public class XopAwareJaxb2Marshaller extends Jaxb2Marshaller {
    
      public XopAwareJaxb2Marshaller() {
        this.setMtomEnabled(true);
      }
    
      protected void initJaxbMarshaller(final Marshaller marshaller) throws JAXBException {
        super.initJaxbMarshaller(marshaller);
    
        marshaller.setEventHandler(new XopAwareMarshallingValidationEventHandler(marshaller.getEventHandler()));
      }
    
      protected void initJaxbUnmarshaller(final Unmarshaller unmarshaller) throws JAXBException {
        super.initJaxbUnmarshaller(unmarshaller);
    
        unmarshaller.setEventHandler(new XopAwareUnmarshallingValidationEventHandler(unmarshaller
            .getEventHandler()));
      }
    }