JAX-WS, wsimport, and the error “MustUnderstand headers … not understood”

Following the previous adventure surrounding collision in the object factory class, this time around we take it a step further. Instead of simply using xjc command from JAXB and marshall/unmarshall elements into the SOAP envelope, we thought lets use wsimport against the WSDL instead. Again, wsimport is part of standard JDK.

The tool wsimport also makes use of xjc and therefore the collision issue also occurred. Thankfully, we can specify the binding (-b) argument with wsimport as well. Even better, use the following maven plugin:

<plugin>
    <groupId>org.jvnet.jax-ws-commons</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.1</version>
    <executions>
        <execution>
            <goals>
                <goal>wsimport</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <target>2.1</target>
        <extension>true</extension>
        <bindingDirectory>src/main/resources</bindingDirectory>
        <bindingFiles>
            <bindingFile>bindings.xjb</bindingFile>
        </bindingFiles>
        <wsdlDirectory>src/main/resources</wsdlDirectory>
        <wsdlFiles>
            <wsdlFile>schemas.ppsr.gov.au.2011.04.services.wsdl</wsdlFile>
        </wsdlFiles>
    </configuration>
</plugin>

All is well and good now.

When we actually hit the end point, the following error is thrown:

27/03/2012 6:40:20 PM com.sun.xml.internal.ws.protocol.soap.MUTube getMisUnderstoodHeaders
INFO: Element not understood={http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security
Exception: MustUnderstand headers:[{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood

The interesting thing is that invoking using soapUI, all worked okay, and the following response returned:


<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <wsse:Security
            xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
            S:mustUnderstand="1">
            <wsse:UsernameToken 
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                wsu:Id="UsernameToken-1">
                <wsse:Username><!-- omitted --></wsse:Username>
                <wsse:Password 
                    Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
                    <!-- omitted -->
                </wsse:Password>
                <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
                    <!-- omitted -->
                </wsse:Nonce>
                <wsu:Created>2012-03-27T07:40:19.628Z</wsu:Created>
            </wsse:UsernameToken>
        </wsse:Security>
    </S:Header>
    <S:Body>
        <ns2:PingRequestMessage
            xmlns="http://schemas.ppsr.gov.au/2011/04/data"
            xmlns:ns2="http://schemas.ppsr.gov.au/2011/04/services"
            xmlns:ns3="http://schemas.microsoft.com/2003/10/Serialization/Arrays"
            xmlns:ns4="http://schemas.ppsr.gov.au/faults" />
    </S:Body>
</S:Envelope>

After further reading, we discover it’s mostly related to the mustUnderstand and the way we use javax.xml.ws.handler.soap.SOAPHandler (White 2010). Reading javadoc itself is not enough, since the getHeaders() method simply says “Gets the header blocks that can be processed by this Handler instance” (Oracle 2011).

Therefore, to resolve this issue, our handler must understand the response, and that’s done by “handling” the response as follows:

PPSRSOAPMessageHandler

package com.wordpress.dwuysan;

import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class PPSRSOApMessageHandler implements SOAPHandler<SOAPMessageContext> {
    // ... the rest of the code omitted

    @Override
    public Set<QName> getHeaders() {
        final QName securityHeader = new QName(
            "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
            "Security",
            "wsse");

        // ... "understand" the response, very complex logic goes here
        // ... "understand" the response, very complex logic goes here
        // ... "understand" the response, very complex logic goes here

        final HashSet headers = new HashSet();
        headers.add(securityHeader);

        // notify the runtime that this is handled
        return headers;
    }

    // ... the rest of the code omitted
}

References:
Oracle, 2011, SOAPHandler (Java EE 6), accessed 02 April 2012.

White, J, 2010, Working with Headers in JAX-WS SOAPHandlers, accessed 02 April 2012.