Version 2

    Customizing SOAP header elements

     

    At times you will need to interact with other WS providers like .Net. When interacting with these providers you notice that they need some additional element to be present in the SOAPHeader. There could be incompatibilities too such as the all elements should be prefixed with namespace for .Net whereas in JBossWS it need not. So the message sent from JBossWS fails in .Net. In all the above situations you can manipulate the SOAPHeader so that the required artifacts are added or modified. This article is not the place to debate on whether a particular Header element is standard or not rather this enables you to inter-operate your corporate stack with JBossWS.

     

    Prerequisites

    The reader is assumed to have knowledge of how to write JBossWS Handlers. Please refer the user guide here for more information. On a simple note you can add the client-side handlers with the following code:

     

    List<Handler> handlerChain = new ArrayList<Handler>();
    handlerChain.add(new MyCustomHandler());
    BindingProvider bp = (BindingProvider)port;
    bp.getBinding().setHandlerChain(handlerChain);

    The GenericSOAPHandler

    In all the above use cases you create a custom Handler extending from the GenericSOAPHandler class. This is convenient as you need to define only the relevant methods you need to be using.

    public class NonceHandler extends GenericSOAPHandler
    {
         protected boolean handleInbound(MessageContext msgContext)
         {
              //Custom code here
              return true;
         }

         protected boolean handleOutbound(MessageContext msgContext)
         {
              //Custom code here
              return true;
         }
    }

     

    Accessing the SOAPHeader

    You can acquire a reference to SOAHeader like this:

     

    protected boolean handleOutbound(MessageContext msgContext)
    {
         SOAPMessage soapMessage = ((SOAPMessageContext)msgContext).getMessage();
         SOAPHeader soapHeader = soapMessage.getSOAPHeader();

         return true;
    }

     

    Enumerating Header Elements

    Calling the examineAllHeaderElements on the SOAPHeader retrives an iterator of SOAPHeaderElements.

    Iterator<SOAPHeaderElement> headerElements = soapHeader.examineAllHeaderElements();
    while (headerElements.hasNext())
    {
         SOAPHeaderElement hElement = headerElements.next();
         Name hName = hElement.getElementName();

    }

     

    Manipulating Header Elements

    Here in this sample I have added a namespace prefix "ns1" to all SOAPHeader elements. It is a recursive method that adds the prefix only on SOAPElement types. Of course this is the simplest form of manipulating the Header element.

    private void addNamespace(Iterator<Node> elements)
    {
       Iterator prefixes;
       while (elements.hasNext())
       {
          Node node = elements.next();
          if (node instanceof SOAPElement)
          {
             SOAPElement bElement = (SOAPElement)node;
             prefixes = bElement.getNamespacePrefixes();
             if (!prefixes.hasNext())
             {
                QName qName = bElement.getElementQName();
                try
                {
                   String ns = qName.getNamespaceURI();
                   if (ns.equals(""))
                   {
                      ns = bElement.getParentElement().getElementQName().getNamespaceURI();
                   }
                   bElement.addNamespaceDeclaration("ns1",ns);
                }
                catch (SOAPException se)
                {
                   se.printStackTrace();
                }
             }
             addNamespace(bElement.getChildElements());
          }
       }
    }

     

    Adding new Header Elements

    On a serious note, as of writing this article, JBossWS 2.x does not yet support Nonce header elements when using Username token authentication. But I have seen .Net services reject them if a Nonce header is not found. To overcome this you can use the following technique and add the Nonce. The complete code is listed below.

    import org.jboss.ws.extensions.security.Constants;

    private static SecureRandom pseudoRng;
    private static QName securityQname = new QName(Constants.WSSE_NS, "Security");
    private static QName usernameTokenQname = new QName(Constants.WSSE_NS, "UsernameToken");
    private static Name nonceElName = new NameImpl("Nonce", Constants.WSSE_PREFIX, Constants.WSSE_NS);
    private static QName passwordQname = new QName(Constants.WSSE_NS, "Password");

    static
    {
       try
       {
          pseudoRng = SecureRandom.getInstance("SHA1PRNG");
          pseudoRng.setSeed(System.currentTimeMillis());
       }
       catch (NoSuchAlgorithmException e)
       {
       }
    }


    private String generateNonce()
    {
       byte[] bytes = new byte[32];
       pseudoRng.nextBytes(bytes);
       return Base64.encodeBytes(bytes);
    }

    protected boolean
    handleOutbound(MessageContext msgContext)
    {
       try
       {
          SOAPMessage soapMessage = ((SOAPMessageContext)msgContext).getMessage();
          SOAPHeader soapHeader = soapMessage.getSOAPHeader();
          Iterator<SOAPHeaderElement> headerElements = soapHeader.examineAllHeaderElements();
          while (headerElements.hasNext())
          {
             SOAPHeaderElement hElement = headerElements.next();
             Name hName = hElement.getElementName();
             if (securityQname.equals(new QName(hName.getURI(), hName.getLocalName())))
             {
                //We found the security header
                Iterator<SOAPElement> elements = hElement.getChildElements();
                while (elements.hasNext())
                {
                   SOAPElement element = elements.next();
                   Name name = element.getElementName();
                   if (usernameTokenQname.equals(new QName(name.getURI(), name.getLocalName())))
                   {
                      //We found the username token add the nonce now
                      SOAPElement nonceEl = element.addChildElement(nonceElName);
                      nonceEl.setValue(generateNonce());
                      Iterator<SOAPElement> unelements = element.getChildElements();
                      while (unelements.hasNext())
                      {
                         SOAPElement uelement = unelements.next();
                         Name pname = uelement.getElementName();
                         if (passwordQname.equals(new QName(pname.getURI(), pname.getLocalName())))
                         {
                            ((Node)uelement).getAttributes().removeNamedItem("Type");
                         }
                      }
                    }
                 }
             }
          }
       }
       catch (Exception e)
       {
          e.printStackTrace();
       }

       return true;
    }

     

    Conclusion

    As depicted and described above any WS stack can inter-operate with JBossWS as long as their needed Header elements are found. This can easily be achieved by adding the necessary header elements for the Outgoing or Incoming message packets.