If you receive null for every attribute to your SOAP requests in Spring, after upgrading JDK, this is what you need to do

If you have a SOAP service written in Java using Spring, you have written methods that are called with Java objects representing the body of the SOAP request. If, after upgrading from Java 8 to Java 11, you see that every attribute of those Java objects is null, adding the following to your Maven file is the solution:

<plugin>
  <groupId>com.google.code.maven-replacer-plugin</groupId>
  <artifactId>maven-replacer-plugin</artifactId>
  <version>1.3.5</version>
  <executions>
    <execution>
      <id>make-namespaces-visible-to-soap</id>
      <phase>process-sources</phase>
      <goals><goal>replace</goal></goals>
      <configuration>
        <includes>
          <include>target/generated-sources/jaxb/com/myproject/package-info.java</include>
        </includes>
        <replacements>
          <replacement>
            <token>@javax.xml.bind.annotation.XmlSchema\(</token>
            <value>@javax.xml.bind.annotation.XmlSchema(elementFormDefault = 
               javax.xml.bind.annotation.XmlNsForm.QUALIFIED, </value>
          </replacement>
        </replacements>
      </configuration>
    </execution>
  </executions>
</plugin>

Explanation

Since JDK9, Project Jigsaw is part of the JDK. That means, that certain functionality which was previously provided in the JDK is no longer available by default. One such functionality is the XJC tool and JAXB library used by Spring to provide SOAP services:

These were previously included in the JDK, and are now available on Maven to be added to your pom.xml file.

I don't know what versions of these two modules were included in JDK8 (I suppose they were just "JDK8 version") but the versions of JAXB runtime on Maven Central (needed at runtime) are 2.2, 2.3 and 2.4. (I tried all of them.)

SOAP requests use namespaces to differentiate the SOAP envelope (containing the request), the security information, and your body. This is a good thing, and anyway even if it wasn't, Spring complains otherwise:

Caused by: java.lang.IllegalArgumentException: 
  class path resource [sap.xsd] has no targetNamespace

After a full day of debugging and reading source code, the conclusion I came to is that this is what's going on:

  1. When the SOAP request is sent, each element contains the appropriate namespace. This is interpreted correctly by Spring.
  2. JAXB, at runtime, attempts to take values from the XML supplied in the SOAP request, and place them into objects (of classes generated from the XSD by XJC).
  3. JAXB does not have access to the XSD definition at runtime; all it has access to is the generated classes. It attempts to read the XML namespace out of these generated classes.
  4. The XML namespace is written by XJC into a generated package-info.java file next to the Java classes representing each XSD element. This file has an @XmlSchema annotation, which correctly contains the XML namespace.
  5. However, JAXB at runtime fails to read this namespace from the @XmlSchema in the package-info.java, meaning it thinks that the classes to be populated from the request represent XML without a namespace.
  6. Seeing that none of the elements in the SOAP request (with namespace) match the elements in the Java objects to be populated (without namespace), it does not populate them.
  7. Therefore, your Java service method is called with an object of the right class, but whose every attribute is null.

The code to extract, from the generated Java class, the XML attribute that each Java attribute represents, is here. You can see it checks for the package annotation @XmlSchema.

The @XmlSchema annotation has an attribute elementFormDefault. This is default UNSET.

If it's UNSET, the JAXB code ignores the namespace extracted from the @XmlSchema. That's the problem.

I don't understand why you would want to ignore namespaces. Perhaps to workaround some other bug?

There is no way I can find to alter the XJC generation process to add that attribute to the @XmlSchema annotation on the generated package-info.java, so I alter the files with that "sed"-like file manipulation.

Other info

P.S. I recently created a nerdy privacy-respecting tool called When Will I Run Out Of Money? It's available for free if you want to check it out.

This article is © Adrian Smith.
It was originally published on 15 Mar 2019
More on: Spring | Java