Uploaded image for project: 'Spring Web Services'
  1. Spring Web Services
  2. SWS-905

Improve the experience to enable MTOM with JAXB

    Details

    • Type: Improvement
    • Status: Reopened
    • Priority: Minor
    • Resolution: Unresolved
    • Affects Version/s: 2.2.1
    • Fix Version/s: 3.1
    • Component/s: Core, Documentation, Samples
    • Labels:
      None

      Description

      I had a very poor experience trying to enable MTOM attachment handling on a receiving endpoint expecting JAXB input parameters. Please let me write the whole story.

      I have to receive web service requests as defined by the following contract: http://www.fatturapa.gov.it/export/fatturazione/sdi/ws/trasmissione/v1.0/TrasmissioneFatture_v1.1.wsdl
      and the imported schema: http://www.fatturapa.gov.it/export/fatturazione/sdi/ws/trasmissione/v1.0/TrasmissioneTypes_v1.1.xsd

      With the following configuration I could make it almost work:

      <?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:web-services="http://www.springframework.org/schema/web-services"
      	xmlns:context="http://www.springframework.org/schema/context"
      	xsi:schemaLocation="http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
      		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
      		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"
      	default-lazy-init="true">
       
      	<context:component-scan base-package="mypackage" />
       
      	<web-services:annotation-driven />
       
      	<web-services:static-wsdl id="TrasmissioneFatture"
      		location="classpath:/wsdl/TrasmissioneFatture_v1.1.wsdl" />
       
      	<bean id="TrasmissioneTypes_v1.1" class="org.springframework.xml.xsd.SimpleXsdSchema">
      		<property name="xsd" value="classpath:/wsdl/TrasmissioneTypes_v1.1.xsd" />
      	</bean>
      </beans>

      @Endpoint
      public class TrasmissioneFattureEndpoint {
       
        @SoapAction("http://www.fatturapa.it/TrasmissioneFatture/RicevutaConsegna")
        public void ricevutaConsegna(@RequestPayload FileSdIType input) {
          // processing
        }

      where FileSdiType is a Java class generated by running the JAXB compiler XJC against the above XSD file. Please note that FileSdiType is annotated with @XmlType and not with @XmlRootElement (actually, no generated class has the @XmlRootElement annotation...).
      Contrary to what the reference documentation says, unmarshalling for FileSdType works even if it is not annotated with @XmlRootElement, but that's fine for me: the code and configuration are very brief and neat!

      This almost worked perfectly. I say "almost" because one of the elements of FileSdType schema type is a binary attachment. By trying to retrieve its content in the endpoing implementation using the InputStream on the DataHandler exposed by the corresponding Java type, all works fine as long as the client sends the attachment as an inline base64 binary string.
      But if the client uses MTOM, the input stream reads nothing.

      The first reaction was: well, I would have expected it to work out of the box, since MTOM is said to be the de-facto standard for attachments.
      The second thought was: let's look at the reference documentation better... but nothing relevant is said for this use case.
      The third was: let's search with Google.

      The actual solution was given by this StackOverflow answer: http://stackoverflow.com/questions/11316023/spring-ws-webservice-with-mtom-attachement-hello-world-test/11576245#11576245
      That is: define a Jaxb2Marshaller with enabled MTOM, define a MarshallingPayloadMethodProcessor that uses it, define a DefaultMethodEndpointAdapter that uses that processor, define a message receiver of type SoapMessageDispatcher that uses that method endpoint adapter and make the MessageDispatcherServlet use that message receiver.

      With all of this lengthy configuration, I could make it work, but:

      • there's a lot of (manual) configuration to set up and there's no indication in the reference guide on how to do this
      • after founding the StackOverflow question, I discovered there's a MTOM example project in the spring-ws-samples GitHub project, but:
        • I couldn't find any link to this project in the Spring Web Services website on spring.io (I found it by chance using Google...)
        • I couldn't find any mention to that samples project in the reference guide
        • the sample uses Java configuration (not XML), so a bit of translation is required in my case; nevertheless, I couldn't find where the message receiver is configured, which is an essential part of the configuration (I debugged my configuration and, without it, the default method endpoint adapter created by the Spring WS namespace tags is still preferred and used, so MTOM attachment handling doesn't work)
      • while org.springframework.ws.server.endpoint.adapter.method.jaxb.XmlRootElementPayloadMethodProcessor.supportsRequestPayloadParameter(MethodParameter) checks for both the presence of @XmlRootElement and @XmlType, org.springframework.oxm.jaxb.Jaxb2Marshaller.supportsInternal(Class<?>, boolean) only checks for the former annotation and not for the latter, so I had to change my endpoint method signature to use JaxbElement<FileSdIType> (which IMHO introduces useless noise in the endpoint implementation)
      • another (smaller) annoyance is that if I define my Jaxb2Marshaller I have to explicitly set its context path (or packages/classes to scan/support), while with the default non-MTOM enabled configuration this is not required, because the appropriate Jaxb2 marshaller is found automatically (I think it's thanks to org.springframework.ws.server.endpoint.adapter.method.jaxb.AbstractJaxb2PayloadMethodProcessor.createUnmarshaller(Class<?>) and org.springframework.ws.server.endpoint.adapter.method.jaxb.AbstractJaxb2PayloadMethodProcessor.getJaxbContext(Class<?>))

      I think all of this should be improved, or at least documented. Also, if the requirement to explicitely enable MTOM on the marshaller is desirable, why this shouldn't just work?

      <web-services:annotation-driven marshaller="marshaller" unmarshaller="marshaller" />

      where marshaller is my MTOM-enabled Jaxb2Marshaller? In my tests, this does not work (i.e.: specifying the marshaller in the <web-services:annotation-driven> tag is unexpectedly totally useless for this use case).

      Probably this is related to SWS-825, which was closed as deferred probably because of a lack of resources, However, in the short-mid term, if this could not be improved, at least please document it in the reference guide: I don't think the need for a MTOM+JAXB working solution is so uncommon.

        Issue Links

          Activity

            People

            • Assignee:
              gregturn Greg Turnquist
              Reporter:
              mauromol Mauro Molinari
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: