Saturday, March 30, 2013

Using WS-Adressing in JAX-WS 2.x client

This is quick code example presented how to setup WS-Addressing policy in JAX-WS applications.

Let's start form server side code. The addressing is enabled by default, so if client will send the message with addresing header the sever will reply correctly, but the policy is not included into generated WSDL. To explicite enable the support of WS-Addressing you need to annoted your service implementation bean (SIB) wih @Addressing. So example webservice implementation code may looks like this:
@Addressing(required = true)
public class HelloWorld {

  public String sayHelloWorldFrom(String from) {
    String result = "Hello, world, from " + from;
    return result;
And that is all. The binding element of JAX-WS generated WSDL will have UsingAddressing element:
<definitions xmlns:wsaw="" 

<!--types, messages, portType are ommited for readability-->
    <binding name="HelloWorldPortBinding" type="tns:HelloWorld">
        <wsaw:UsingAddressing required="true"/>
        <soap:binding transport="" style="document"></soap:binding>
        <operation name="sayHelloWorldFrom">
            <soap:operation soapAction=""></soap:operation>
                <soap:body use="literal"></soap:body>
                <soap:body use="literal"></soap:body>
    <service name="HelloWorldService">
        <port name="HelloWorldPort" binding="tns:HelloWorldPortBinding">
            <soap:address location=""></soap:address>
Now, what need you to do enable addressing feature on client? It depends if you use the wsimport service and port artifacts then the answer is: absolutly nothing! Even if you generate it from WSDL without UsingAddresing element as there is no changes in JAXB classes. Only runtime artifacts are affected. Here is the client code:
public class JaxWsClient {
    public static void main(String[] args){
        HelloWorldService service=new HelloWorldService();
        HelloWorld port=service.getHelloWorldPort();
After running it the following messages will be exchanged:
<S:Envelope xmlns:S="">
        <To xmlns=""></To>
        <Action xmlns="">http://example/HelloWorld/sayHelloWorldFromRequest</Action>
        <ReplyTo xmlns="">
        <MessageID xmlns="">uuid:d54cb478-fdd1-4495-84df-2fde515a4591</MessageID>
        <ns2:sayHelloWorldFrom xmlns:ns2="http://example/">
and response:
<S:Envelope xmlns:S="">
        <To xmlns=""></To>
        <Action xmlns="">http://example/HelloWorld/sayHelloWorldFromResponse</Action>
        <MessageID xmlns="">uuid:a95c9cff-7b9f-4f23-9608-fe1ca9ce1d75</MessageID>
        <RelatesTo xmlns="">uuid:d54cb478-fdd1-4495-84df-2fde515a4591</RelatesTo>
        <ns2:sayHelloWorldFromResponse xmlns:ns2="http://example/">
            <return>Hello, world, from JaxWxClient</return>

However if you try to develop dynamic client using dispacher, for example:
public class JaxWsClient {
    public static void main(String[] args) throws SOAPException {
        QName serviceName=new QName("http://example/","HelloWorldService");
        QName portName=new QName("http://example/","HelloWorldPortBinding");
        Service service = Service.create(serviceName);
        service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING,"http://localhost:9000/HelloWorld");
        Dispatch dispatch = service.createDispatch(portName,SOAPMessage.class,Service.Mode.MESSAGE);
        MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
        SOAPMessage request = mf.createMessage();
        SOAPPart part = request.getSOAPPart();
        SOAPEnvelope env = part.getEnvelope();
        SOAPBody body = env.getBody();
        SOAPBodyElement element = body.addBodyElement(new QName("http://example/","sayHelloWorldFrom"));
        SOAPElement arg=element.addChildElement(new QName("http://example/","arg"));
        SOAPMessage response=dispatch.invoke(request);
Then when you try to run it you will receive the SOAP fault:
<S:Envelope xmlns:S="">
        <FaultDetail xmlns="">
        <To xmlns=""></To>
        <Action xmlns=""></Action>
        <MessageID xmlns="">uuid:713f84a8-2dbf-44a7-936c-d445bfb890df</MessageID>
        <SOAP-ENV:Fault xmlns:SOAP-ENV="">
            <faultcode xmlns:SOAP-ENV="" 
                       xmlns:ns0="" xmlns="">ns0:InvalidCardinality</faultcode>
            <faultstring xmlns="">A header representing a Message Addressing Property 
                is not valid and the message cannot be processed</faultstring>
It is required to enable the addressing support on created dispatcher and set required correct SOAPAction. To do this you need to change the client code to:
Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,SOAPMessage.class,Service.Mode.MESSAGE,new AddressingFeature());
and add:
The value of Action used here is the default one which is created in case of absence the custom one. It is build by appending together: target namspace, service name and operation name and string "Response" or "Request". You may find it in WSDL in Action attribute of the input/output/fault element in binding section. After those changes the request will look like the previously presented. You not required to create the address header by yourself. But wait a moment! SOAPAction HTTP Header and WS-Addressing Action header are two different things! Yes, they are. Moreover SOAPAction is set for the operation, and WSA Action may be different for each message in that specific operation. But when it comes to request where we use the SOAPAction and WSA Action then the recommendation requires that both of them are equal or SOAPAction was set to empty string - "". 

1 komentarze:

Sathish Kumar said...

Nice code explanation.

Post a Comment