Retrieving the SOAPAction in a CXF Provider

I was writing a WSDL-first SOAP 1.1 service (server) using Apache CXF. I needed to directly access the XML DOM of the SOAP Header and Body, so I did not want to use the standard JAXB data binding. My solution was to implement a Provider using message mode. However, retrieving the SOAPAction inside the Provider’s invoke(SOAPMessage msg) method was not intuitive to me.

The WSDL for this service was produced by a standards body, so I tried to avoid modify it. Each operation in the WSDL specified a soapAction, therefore I assumed I needed the value of the SOAPAction HTTP header to process the message.

After further digging into the SOAP 1.1 Specification, it looks like the SOAPAction is more or less optional. The HTTP header is required, but it is not required to have a value. Therefore, I suspect to make my service as robust as possible, I may not require the header and instead look at the name of the first element in the SOAP Body to determine the operation.

In any case, to get the value of the SOAPAction header, here is what I did.

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import org.apache.cxf.binding.soap.SoapBindingConstants;

@WebService(/* details specified here */)
@ServiceMode(value=Service.Mode.MESSAGE)
public class MyServer implements Provider<SOAPMessage> {
  private WebServiceContext m_webServiceContext;

  @Resource
  public void setWebServiceContext(WebServiceContext context) {
    m_webServiceContext = context;
  }
  
  public SOAPMessage invoke(SOAPMessage request) {
    final String soapAction = (String) m_webServiceContext
             .getMessageContext()
             .get(SoapBindingConstants.SOAP_ACTION);
  }
}

The annotation @Resource causes the method setWebServiceContext to be called when the service starts. WebServiceContext contains values stored in ThreadLocal fields which allow you to get context about the message being processed inside invoke().

I found this use of “context” very unintuitive. I would expect that injected dependency to provide context for the entire Provider, not a specific method invocation. Any context specific to the method invocation I would expect to arrive as an argument to the invoke() method. It appears this is the way JAX-WS was designed.

Note that the context contains many other properties that may be useful in your coding and debugging.

Hopefully this saved you hours of head scratching.