Thursday, July 3, 2014

Overriding WS-Addressing headers in JAX-WS


This is going to be quick and to the point, let's suppose you have a web service you have to call that requires WS-Addressing headers.  Usually, if the WSDL is created with the <wsaw:UsingAddressing wsdl:required="true"/> under the binding section, then the generated JAX-WS client will automatically add the WS-Addressing headers for you.

The problem I've had is that with the IBM Websphere runtime, you have no easy way to override the default parameters.  I've tried many different ways, but was only able to get one way to work (which I'll share in just a minute).  I originally thought it would be pretty simple... just create a handler that intercepts the message as it's going out and modify the appropriate SOAP Header element.  But the problem with this approach is that (at least in the IBM runtime) the WS-Addressing header information isn't created and added to the soap request until after it goes through the handler chains.  So, in a handler, you won't see any of the WS-Addressing headers because they haven't been added yet.  However, I did find that you if you add your own WS-Addressing header in a handler, the JAX-WS runtime won't try to create it.  So, I ended up creating a handler and just creating the WS-Addressing header I cared about overriding and set it there.

Here's the handler code:


 @Override  
      public boolean handleMessage(SOAPMessageContext smc) {  
           Boolean outBoundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);  
           if (outBoundProperty.booleanValue()) {  
                SOAPFactory factory = null;  
                javax.xml.soap.SOAPElement element = null;  
                try {  
                     factory = SOAPFactory.newInstance();  
                     element = factory.createElement(new QName("http://www.w3.org/2005/08/addressing", "To"));  
                     element.setTextContent("IT");  
                     // check if there's a SOAP Header  
                     SOAPHeader header = smc.getMessage().getSOAPPart().getEnvelope().getHeader();  
                     if (header == null) {  
                          // create header since there isn't one  
                          header = smc.getMessage().getSOAPPart().getEnvelope().addHeader();  
                     }  
                     header.addChildElement(element);  
                } catch (SOAPException e) {  
                     // TODO: Do better error handling and logging  
                     e.printStackTrace();  
                }  
           }  
           return true;  
      }  

This handler creates a "To" element then adds it to the header.  It makes sure there's a header to add it to first.

This will produce a header like this:

   <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <To xmlns="http://www.w3.org/2005/08/addressing">IT</To>
      <wsa:MessageID>urn:uuid:e180d2ff-7254-45eb-8bd8-f0058bb61ec7</wsa:MessageID>
      <wsa:Action>http://ws.cdyne.com/CheckTextBody</wsa:Action>
   </soapenv:Header>


With a handler like this you can easily override the parameters.  I'd be interested to hear of other ways.  Perhaps easier ways of doing the same.  Or let me know what you find out.  My experience is with Websphere 8.