SOAPHandler example

JAX-WS clients must include an object that implements SOAPHandler<SOAPMessageContext>.

The following code provides an example of this object.

/****************************************************************
** Licensed Materials - Property of IBM
** IBM SPSS Products: Collaboration and Deployment Services
** © Copyright IBM Corp. 2000, 2013
** US Government Users Restricted Rights - Use, duplication or 
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
*****************************************************************/

import java.util.Collections;
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.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

/**
 * This is a SOAP handler that applies a security header and a language header to a SOAP message.
 */
public class SecurityHandler implements SOAPHandler<SOAPMessageContext>
{
    // WS-Security header values
    public static final String SECURITY = "Security";
    public static final String USERNAME_TOKEN = "UsernameToken";
    public static final String USERNAME = "Username";
    public static final String PASSWORD = "Password";
    public static final String WS_SECURITY_NAMESPACE = 
       "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
    
    // prefixes
    public static final String WSSE_PREFIX = "wsse"; // ws service security
    public static final String SPSS_PREFIX = "spss"; // spss prefix
    
    // SPSS custom language header values
    public static final String SPSS_HEADER_NAMESPACE = "http://xml.spss.com/ws/headers";
    public static final String CLIENT_ACCEPT_LANGUAGE_HEADER = "client-accept-language";

    private String i_username;
    private String i_password;
    private String i_acceptLanguage;
    
    /**
     * Creates a security and language handler
     * @param username A user name to access the web service. Cannot be null.
     * @param password A password to access the web service. Cannot be null.
     * @param acceptLanguage The language that should be used by the web service.
     * This value should be formatted according to the HTTP specification regarding 
     * the Accept-Language HTTP header (e.g. en-US;q=1.0, en;q=0.8)
     * If the value is null, the language header will not be added.
     */
    public SecurityHandler(String username, String password, String acceptLanguage)
    {
        i_username = username;
        i_password = password;
        i_acceptLanguage = acceptLanguage;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context)
    {
        // Apply this handler to only outbound traffic
        if((Boolean)context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY))
        {
            // get the message
            SOAPMessage message = context.getMessage();
            try
            {
                // get the message header
                SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
                SOAPHeader header = envelope.getHeader();
                if (header == null)
                {
                    header = envelope.addHeader();
                }
                
                // add the UsernameToken header
                header.addChildElement(createUsernameTokenSecurityHeader());
                // assuming the language was provided, apply the custom language header
                if(i_acceptLanguage != null)
                {
                    header.addChildElement(createLanguageHeader());
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        // allow any other handler to execute
        return true;
    }
    
    /**
     * This method creates a custom language header, which allows the scoring service
     * to use the given language if possible.
     * @return A custom language header
     * @throws Exception
     */
    private SOAPElement createLanguageHeader() throws Exception
    {
        SOAPFactory factory = SOAPFactory.newInstance();

        // create a custom language header
        SOAPElement languageHeader = 
           factory.createElement(CLIENT_ACCEPT_LANGUAGE_HEADER,SPSS_PREFIX,SPSS_HEADER_NAMESPACE);
        
        // include the language text
        languageHeader.addTextNode(i_acceptLanguage);
        
        return languageHeader;
    }

    /**
     * Creates the WS-Security SOAP header for UsernameToken as SOAPElement.
     *
     * @return the WS-Security SOAP header for UsernameToken
     * @throws Exception as appropriate
     */
    private SOAPElement createUsernameTokenSecurityHeader() throws Exception
    {
        SOAPFactory factory = SOAPFactory.newInstance();

        // create a UsernameToken element
        SOAPElement usernameToken = 
          factory.createElement(USERNAME_TOKEN, WSSE_PREFIX, WS_SECURITY_NAMESPACE);

        // add the username element
        SOAPElement usernameElement = 
          factory.createElement(USERNAME, WSSE_PREFIX, WS_SECURITY_NAMESPACE);
        usernameElement.addTextNode(i_username);
        usernameToken.addChildElement(usernameElement);

        // add the password element
        SOAPElement passwordElement = 
          factory.createElement(PASSWORD, WSSE_PREFIX, WS_SECURITY_NAMESPACE);
        passwordElement.addTextNode(i_password);
        usernameToken.addChildElement(passwordElement);

        // create the Security Header
        SOAPElement securityHeader = 
          factory.createElement(SECURITY, WSSE_PREFIX, WS_SECURITY_NAMESPACE);
        securityHeader.addChildElement(usernameToken);

        return securityHeader;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context)
    {
        // allow any other handler to execute
        return true;
    }

    @Override
    public void close(MessageContext context)
    {
        // do nothing
    }

    @Override
    public Set<QName> getHeaders()
    {
        return Collections.emptySet();
    }

}