The Source for Java Technology Collaboration
User: Password:



Kohsuke Kawaguchi

Kohsuke Kawaguchi's Blog

Adding SOAP headers in the JAX-WS RI is easier in 2.1 EA3

Posted by kohsuke on November 20, 2006 at 03:07 PM | Comments (23)

One of the additions in the JAX-WS RI 2.1 EA3 is a simple way to add SOAP headers for your request.

The official "portable" way of doing this is that you creaate a SOAPHandler and mess with SAAJ. This works, but Vivek though it's just too much work for such a simple thing one day, and we all agreed. So we quickly put together a better way to do this. Here's how to do this:

When you create a proxy or dispatch object, they implement BindingProvider interface. When you use the JAX-WS RI, you can downcast to WSBindingProvider which defines a few more methods provided only by the JAX-WS RI.

This interface lets you set an arbitrary number of Header object, each representing a SOAP header. You can implement it on your own if you want, but most likely you'd use one of the factory methods defined on Headers class to create one.

import com.sun.xml.ws.developer.WSBindingProvider;

HelloPort port = helloService.getHelloPort();  // or something like that...
WSBindingProvider bp = (WSBindingProvider)port;

bp.setOutboundHeader(
  // simple string value as a header, like <simpleHeader>stringValue</simpleHeader>
  Headers.create(new QName("simpleHeader"),"stringValue"),
  // create a header from JAXB object
  Headers.create(jaxbContext,myJaxbObject)
);

Once set, it will take effect on all the successive methods. If you'd like to see more factory methods on Headers, please let us know!

We are also talking about adding a switch to wsimport so that you won't have to downcast.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Ah! the classic bait (use standard!) and switch (works only with RI) :)

    Posted by: dims on November 21, 2006 at 01:55 AM


  • Hi Dims. I know you placed a smiley there, but, in case somebody misses it... I'd describe it rather as "the RI as a competitive, production-quality implementation".

    A standard is an exercise in deciding what is ready to be standarized, and what is not. But just because we - collectively - are not ready to standarize a solution to a problem does not mean the problem goes away. So pretty much all implementations have proprietary solutions. Including Apache, and including GlassFish's.

    Nothing new here. Look at Tomcat; it was the RI for Servlets for ages, but it also has had proprietary extensions. And this is very much fine as long as it it clear to the customer that s/he is using a proprietary extension. Actually, without experimentation, how are we going to provide input into the next stages of the standarization?

    - eduard/o

    Posted by: pelegri on November 21, 2006 at 04:39 AM

  • Koshuke,

    Should this work with the nightly RI build from December 4th? I get a class cast exception from

    AddressDistanceCalculator port = new AddressDistanceCalculator();
    WSBindingProvider bp = (WSBindingProvider)port;

    package com.tbh.integration.strikeiron.distance;

    import com.strikeiron.*;
    import com.strikeiron.ws.LicenseInfo;
    import com.strikeiron.ws.RegisteredUser;
    import com.sun.xml.ws.developer.WSBindingProvider;
    import com.sun.xml.ws.api.message.Headers;
    import com.sun.xml.ws.api.client.WSBindingProvider;
    import junit.framework.TestCase;

    import javax.xml.ws.WebServiceRef;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.JAXBContext;
    import javax.xml.namespace.QName;
    import java.io.StringWriter;

    public class DistanceCalculatorTest extends TestCase {
    @WebServiceRef(wsdlLocation =
    "http://ws.strikeiron.com/AddressDistanceCalculator?WSDL")
    static AddressDistanceCalculator service;

    public static void main(String[] args) {
    DistanceCalculatorTest instance = new DistanceCalculatorTest();
    instance.testCalculator();
    }

    public void testCalculator() {
    try {
    AddressDistanceCalculator port = new AddressDistanceCalculator();
    WSBindingProvider bp = (WSBindingProvider)port;
    JAXBContext jaxbContext = JAXBContext.newInstance("com.strikeiron.ws");
    Marshaller marshaller = jaxbContext.createMarshaller();
    StringWriter outStream = new StringWriter();

    LicenseInfo licenseInfo = new LicenseInfo();
    RegisteredUser registeredUser = licenseInfo.getRegisteredUser();
    registeredUser.setUserID("xxxxxx");
    registeredUser.setPassword("xxxxx");
    marshaller.marshal(licenseInfo, outStream);

    bp.setOutboundHeaders(// simple string value as a header, like stringValue
    Headers.create(new QName("LicenseInfo"), outStream.toString())
    );

    AddressDistanceCalculatorSoap calc = port.getAddressDistanceCalculatorSoap();
    System.out.println(" calc " + calc);

    AddressInput addressInput = new AddressInput();
    addressInput.setAddress1("24 C St");
    addressInput.setCityStateZip("San Rafael, CA, 94903");
    addressInput.setCountry(CountryCode.US);

    AddressInput addressOutput = new AddressInput();

    calc.addressToAddressDistance(addressInput, addressOutput, UnitOfMeasure.KILOMETERS);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    Posted by: rdeheer on December 04, 2006 at 10:44 AM

  • I can't see your code, but yes, it should work with Dec 4th nightly of JAX-WS RI 2.1. Please post support questions to the forum in the future.

    Posted by: kohsuke on December 04, 2006 at 04:52 PM

  • Hi Koshuke, client programming is simplied a lot thanx. Is there a simpler approach to get/set SOAP Headers in the service implementation ? I guess the WebServiceContext injected into the implementation has no way to get/set SOAP Headers, is it right ?

    Posted by: yogen on March 23, 2007 at 03:39 PM

  • You can get the headers as discussed here, but I'm not sure about setting part.

    Posted by: kohsuke on March 28, 2007 at 12:52 PM

  • i'm using soap12 binding, but when i use wsimport to generate server its ignoring all my notification operations. any solution to that ??

    Posted by: gfarhat on June 05, 2007 at 02:53 AM

  • Questions like that would be better answered at our forum

    Posted by: kohsuke on June 06, 2007 at 04:39 PM

  • Very nice. Any attempt to make this part of standard api ?
    BTW, beware! the latest version method signature has the ending (s): bp.setOutboundHeaders

    Posted by: ntn on September 11, 2007 at 04:02 PM

  • Which jar do I need to import com.sun.xml.ws.developer.WSBindingProvider;

    Posted by: vatsa82 on October 16, 2007 at 01:53 PM

  • JAX-WS 2.1 runtime jar, which is jaxws-rt.jar

    Posted by: kohsuke on October 16, 2007 at 02:18 PM

  • Thanks, jaxws-rt.jar worked fine. Now the client code cannot resolve Qname. Also do I paste the below line of code as is:

    Headers.create(jaxbContext,myJaxbObject) - cannot resolve jaxbContext

    Sorry, this is my 2nd day in JBoss

    Posted by: vatsa82 on October 16, 2007 at 03:15 PM

  • I resolved QName problem. Can you please paste the whole class which sets the SOAP Header. I do not understand the jaxbContext. I am getting a ClassCast Exception at :
    WSBindingProvider bp = (WSBindingProvider)soap;

    Posted by: vatsa82 on October 16, 2007 at 03:37 PM

  • How do I add string headers without having the symbols escaped?

    Posted by: jbarnum on February 08, 2008 at 10:14 PM

  • Ha ha, ironic, those characters were not escaped so they are not visible. How do I add string headers with xml markup that don't get escaped, ie:

    <RegisteredUser><UserID>myUserId</UserID><Password>myPass</Password></RegisteredUser>

    Posted by: jbarnum on February 08, 2008 at 10:18 PM

  • jbarnum — see the other methods on the Headers class.

    Posted by: kohsuke on February 11, 2008 at 09:28 AM

  • What about http://java.sun.com/products/jdk/faq/faq-sun-packages.html ? I am shocked that you guys can give this kind of hint to Java devs, most of them won't see the dark side of this practice... since it allows to resolve their problem and is even adviced by Sun engineers themselves!!

    Posted by: pisce on March 11, 2008 at 04:47 AM

  • Hi Koshuke,

    The Headers.create(jaxbContext,myJaxbObject) does not seem to work with RI 2.1 as create() method take JAXBRIContext as first argument instead of the JAXBContext and there is no easy way to get a JAXBRIContext instance like one can get JAXBContext instance. JAXBRIContext extends JAXBContext. I am not sure if create( ) method of com.sun.xml.ws.api.message.Headers class used to take a JAXBContext at the time you posted this message.

    If there is some other way which i am missing then i will appreciate if you can point me in the right direction or else the other way of using message handler to add soap headers is known to me but i was willing to use RI extension in my code and go the shortcut route.

    Thanks,
    Watsh

    Posted by: rwatsh on April 03, 2008 at 05:11 AM

  • You'd just have to downcast JAXBContext to JAXBRIContext.

    Posted by: kohsuke on April 03, 2008 at 03:00 PM


  • pisce — The FAQ you cited is for packages in JavaSE. The classes we are discussing here is a part of a stand-alone library (called the JAX-WS RI.) That is something you bundle along with your app, thus it's always available regardless of what future JDKs you run this on.


    So I don't understand why you are concerned.

    Posted by: kohsuke on April 03, 2008 at 03:04 PM

  • Thanks Kohsuke.

    Posted by: rwatsh on April 03, 2008 at 08:42 PM

  • I cited this FAQ as a general principle for portability. If developpers follow the given advice here, they will not be able to change for another JAX-WS implementation without refactoring their code first. This can be a major cost if this point is discovered lately in an application, and will slow down a move to another technology.
    Also, please consider that this page comes first when you google "jaxws soap header", so unexperimented developpers may not be aware of this solution's drawbacks. Maybe this page needs a kind of disclaimer or something?

    Posted by: pisce on April 25, 2008 at 01:17 AM

  • By the way, here's a link to the standard way for attaching a handler to a jaxws proxy on the client side (like the HelloPort above):

    http://dev2dev.bea.com/blog/cindymc/archive/2007/07/using_soap_head.html

    I used it and it works well, although it's of course a bit more work.

    Posted by: pisce on April 25, 2008 at 01:25 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds