651.288.7000 info@intertech.com

By Jim White (Intertech Director of Training and Instructor)

Last week I taught Java Web Services to a great group of gentlemen that were a bit more advanced in their Java/Java EE skills than what I usually have in my classroom.  Therefore, I must admit, they threw some great questions at me.

One question about SOAPHandler (thanks for the great question Kevin) sent me scrambling for a good example on the World Wide Web.  Incredibly, I could not find one.  So, this week’s post is all about the JAX-WS SOAPHandler and how to use its getHeaders() method to deal with mustUnderstand attributes in a SOAP message.

JAX-WS Handlers

In addition to support for web services development, the JAX-WS framework (the latest Java programming language API for creating SOAP-based web services and web service consumers) also provides a handler framework.  Handlers provide a means to inspect and manipulate incoming or outgoing SOAP messages (on both the client as well as server side).  They act as powerful message interceptors that can perform an array of functions such as message transformation, content filtering, tracking, etc.  In fact, handlers are often used in runtime environments to implement web service and SOAP specifications such as WS-Security, WS-ReliableMessaging, etc.  JAX-WS handlers are similar to EJB interceptors or servlet filters.  Handlers, like interceptors and filters, encourage developers to follow the chain of responsibility pattern.

Two Types of Handlers

JAX-WS provides two types of handlers (with two interfaces): LogicalHandler and SOAPHandler.  LogicalHandlers are message protocol neutral. LogicalHandlers only have access to the message payload of a message.  SOAPHandlers are also known as message or protocol handlers.  SOAPHandlers have access to the entire SOAP message.

SOAPHandlers

The SOAPHandler interface requires that the handler class implements four methods: handleMessage, handleFault, close, and getHeaders.  The handleMessage is typically the method of most interest in a handler.  The handleMessage method gets invoked for normal processing by the framework as a message is either sent or received (inbound and outbound).  The handleFault method gets invoked by the framework when a SOAP fault is thrown during message processing.  The close method is invoked by the framework just before dispatch of the message.  The close method allows any cleanup or resource release at the end of handler processing.

Below is an example of a SOAPHandler (minus the getHeaders method which is covered below) that counts the number of elements in both the header and body of a SOAP message.

//required imports here

public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {

@Override
public void close(MessageContext context) {
}

@Override
public boolean handleFault(SOAPMessageContext context) {
return false;
}

@Override
public boolean handleMessage(SOAPMessageContext messagecontext) {
Boolean outbound = (Boolean) messagecontext
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println(“SOAP message departing…”);
} else {
System.out.println(“SOAP message incoming…”);
}
try {
SOAPMessage message = messagecontext.getMessage();
SOAPHeader header = message.getSOAPHeader();
if (header != null) {
Iterator<?> i = header.getChildElements();
System.out.println(“Number of header elements:  ”
+ countElements(i));
}
SOAPBody body = message.getSOAPBody();
if (body != null) {
Iterator<?> i = body.getChildElements();
System.out.println(“Number of body elements:  ”
+ countElements(i));
}
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
return true;
}

private int countElements(Iterator<?> i) {
int count = 0;
while (i.hasNext()) {
count++;
i.next();
}
return count;
}

}

It is the last method, getHeaders, that is the focus of this blog post.  The getHeaders method retrieves the header blocks that can be processed by the handler. It takes no arguments and returns a set of qualified header names.

The getHeaders() Method

So exactly what does the the SOAPHandler’s getHeaders() method do for you?  It is the getHeaders() method that informs the service hosting runtime what SOAP headers the handler is responsible for processing.  It returns the QNames of the outer element of each SOAP header that the handler understands.

MustUnderstand Not Understood!

If you are familiar with SOAP messages, you know that SOAP header elements may come with a mustUnderstand global SOAP attribute.  Valid values for mustUnderstand are true|false (SOAP 1.2) or 1|0 (SOAP 1.1).  This attribute is used to indicate whether or not the web service receiver or intermediary is required to understand the header element before processing the message.  A SOAPHandler can server as an “intermediary” that process the SOAP header before the actual service processes the rest (the body) of the message.

For example, below is a sample SOAP message that contains fake security credentials for use by the server side to authenticate/authorize some service invocation.  Notice the mustUnderstand attribute on both the username and password header elements?

<soapenv:Envelope xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/”>
<soapenv:Header>
<secty:username soapenv:actor=”http://schemas.xmlsoap.org/soap/actor/next” soapenv:mustUnderstand=”1″ xmlns:soapenc=”http://schemas.xmlsoap.org/soap/encoding/” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/” xmlns:secty=”urn:com.intertech.secty”>Jim</secty:username>
<secty:password soapenv:actor=”http://schemas.xmlsoap.org/soap/actor/next” soapenv:mustUnderstand=”1″ xmlns:soapenc=”http://schemas.xmlsoap.org/soap/encoding/” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/” xmlns:secty=”urn:com.intertech.secty”>supersecret</secty:password>
</soapenv:Header>
<soapenv:Body>
<tns:getContact xmlns:soapenc=”http://schemas.xmlsoap.org/soap/encoding/” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:tns=”http://service.intertech.com/”>
<arg0>4</arg0>
</tns:getContact>
</soapenv:Body>
</soapenv:Envelope>

If something does not process the username and password header elements, this message would result in a fault back to the client.  For example, assume that the service did not handle the header elements and the SOAP Handler’s getHeaders() method was coded as shown below.

    @Override
public Set<QName> getHeaders() {
return null;
}

Since nothing handles the username and password headers, the resulting SOAP message to the client would include a MustUnderstand fault code as shown here.

<soap:Envelope
xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/”>
<soap:Body>
<soap:Fault>
<faultcode>soap:MustUnderstand</faultcode>
<faultstring>MustUnderstand headers: [{urn:com.intertech.secty}password, {urn:com.intertech.secty}username] are not understood.</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>

Using getHeaders() to Understand the MustUnderstand

However, the getHeaders() method can be coded to tell the runtime environment that the SOAP handler is going to take care of the mustUnderstand header elements by returning a set of QName (qualified name) objects that match the mustUnderstand header elements.  Below is the new getHeaders() method that handles the username and password headers of this example.

@Override
public Set<QName> getHeaders() {
System.out.println(“Inside SOAP handler of get Headers”);
QName securityUsernameHeader = new QName(“urn:com.intertech.secty”,
“username”);
QName securityPasswordHeader = new QName(“urn:com.intertech.secty”,
“password”);
HashSet<QName> headers = new HashSet<QName>();
headers.add(securityUsernameHeader);
headers.add(securityPasswordHeader);
System.out.println(“got Headers:  ” + headers);
return headers;
}

Now, if the same SOAP element with the username and password header elements was sent to a service and through the HeaderHandler above, a fault is not generated.  It is assumed by the runtime that the header elements were consumed by the handler.  Of course, in a real world SOAP handler, in the handleMessage( ) method you would now want to add code to actually handle these headers.

Thanks again to my students for a great week in class and for the great Java web services questions.  Special thanks to Kevin for bringing up this topic and seeking more information.  Hopefully this example will help him and you wrestle with JAX-WS SOAP Handlers.

If you would like to learn more about Java and Java Web Services, come join me in Intertech’s Complete Java Web Services training.  We’d love to have you.

Like What You've Read?

Subscribe to the Blog.

Every Friday we send that week's content from our Developers via email. Try it out!

Some ad blockers can block the form below.

You have Successfully Subscribed!

Pin It on Pinterest

Share This