Extracting SOAP headers from a Web service response in Spring-WS
A frequently asked question in connection with WebServiceTemplate in Spring-WS is how to extract SOAP headers
from Web service responses. One possible answer is to use a ClientInterceptor. This makes sense because
one of the main use cases for interceptors is SOAP header processing. Client interceptors work well if the information
in the SOAP headers is processed out-of-band, i.e. if it is not returned to the caller of WebServiceTemplate.
However, things are different if the SOAP header data is simply to be returned to the application code so that it
can process that data together with the response payload. In that case, client interceptors are not a good choice.
The reason is that they have no simple way to send back data to the caller of WebServiceTemplate. They can store
data in the MessageContext, but this data can’t be accessed through WebServiceTemplate. One solution is
to store that data in the client interceptor itself and let the application code retrieve it from there. The drawback of that solution is
that this makes the interceptor stateful, which implies that the same WebServiceTemplate instance can no longer be
used concurrently for different requests.
A far simpler and elegant solution is to use a WebServiceMessageExtractor to process the SOAP response.
Consider e.g. a scenario that uses a marshaller/unmarshaller (e.g. JAXB2). Instead of calling
marshalSendAndReceive, one would use the following kind of code:
ResponseAndHeader responseAndHeader = webServiceTemplate.sendAndReceive(
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) throws IOException {
MarshallingUtils.marshal(marshaller, request, message);
}
},
new WebServiceMessageExtractor<ResponseAndHeader>() {
public ResponseAndHeader extractData(WebServiceMessage message) throws IOException {
SoapHeader header = ((SoapMessage)message).getSoapHeader();
Iterator<SoapHeaderElement> it = header.examineHeaderElements(
new QName("urn:test", "ResponseHeader"));
return new ResponseAndHeader(
it.hasNext() ? (ResponseHeader)unmarshaller.unmarshal(it.next().getSource())
: null,
MarshallingUtils.unmarshal(unmarshaller, message));
}
});
Some additional explanations are in order here:
-
request,marshallerandunmarshallerare variables/attributes visible in the scope wheresendAndReceiveis called. Their respective meaning should be obvious. -
The code uses the unmarshaller to process the header element (and unmarshal it into a
ResponseHeaderobject in this example). This is typically what you want to do if you are already using an unmarshaller for the response payload, but it is easy to change the code to use some other technique to process the header. -
MarshallingUtilsis a class provided by Spring-WS. It encapsulates the logic to marshal/unmarshall Web service message and makes that logic easily reusable. It is used byWebServiceTemplateitself, so that in that respect, the code shown above should behave exactly in the same way asmarshalSendAndReceive. -
ResponseAndHeaderis a simple custom class that stores the extracted header together with the response payload. -
The code uses a
WebServiceMessageCallbackto prepare the request. This is necessary becauseWebServiceTemplatedoesn’t have any method that uses a marshaller to prepare the request and aWebServiceMessageExtractorto extract the response.
Note that this approach leaves the WebServiceTemplate stateless, so that a single instance can be used for
concurrent requests. In addition, it requires less code than a custom ClientInterceptor.