Pages

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:
@WebService
@Addressing(required = true)
public class HelloWorld {

  @WebMethod
  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="http://www.w3.org/2006/05/addressing/wsdl" 
....
name="HelloWorldService">

<!--types, messages, portType are ommited for readability-->
    <binding name="HelloWorldPortBinding" type="tns:HelloWorld">
        <wsaw:UsingAddressing required="true"/>
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"></soap:binding>
        <operation name="sayHelloWorldFrom">
            <soap:operation soapAction=""></soap:operation>
            <input>
                <soap:body use="literal"></soap:body>
            </input>
            <output>
                <soap:body use="literal"></soap:body>
            </output>
        </operation>
    </binding>
    <service name="HelloWorldService">
        <port name="HelloWorldPort" binding="tns:HelloWorldPortBinding">
            <soap:address location="http://127.0.0.1:9000/HelloWorldService"></soap:address>
        </port>
    </service>
</definitions>
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();
        System.out.println(port.sayHelloWorldFrom("JaxWxClient"));
    }
}
After running it the following messages will be exchanged:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <To xmlns="http://www.w3.org/2005/08/addressing">http://127.0.0.1:9000/HelloWorld</To>
        <Action xmlns="http://www.w3.org/2005/08/addressing">http://example/HelloWorld/sayHelloWorldFromRequest</Action>
        <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
            <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
        </ReplyTo>
        <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:d54cb478-fdd1-4495-84df-2fde515a4591</MessageID>
    </S:Header>
    <S:Body>
        <ns2:sayHelloWorldFrom xmlns:ns2="http://example/">
            <arg0>JaxWxClient</arg0>
        </ns2:sayHelloWorldFrom>
    </S:Body>
</S:Envelope>
and response:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To>
        <Action xmlns="http://www.w3.org/2005/08/addressing">http://example/HelloWorld/sayHelloWorldFromResponse</Action>
        <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:a95c9cff-7b9f-4f23-9608-fe1ca9ce1d75</MessageID>
        <RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:d54cb478-fdd1-4495-84df-2fde515a4591</RelatesTo>
    </S:Header>
    <S:Body>
        <ns2:sayHelloWorldFromResponse xmlns:ns2="http://example/">
            <return>Hello, world, from JaxWxClient</return>
        </ns2:sayHelloWorldFromResponse>
    </S:Body>
</S:Envelope>

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"));
        arg.addTextNode("JaxWxClient");
        request.saveChanges();
        SOAPMessage response=dispatch.invoke(request);
    }
}
Then when you try to run it you will receive the SOAP fault:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <FaultDetail xmlns="http://www.w3.org/2005/08/addressing">
            <ProblemHeaderQName>{http://www.w3.org/2005/08/addressing}Action</ProblemHeaderQName>
        </FaultDetail>
        <To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To>
        <Action xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/fault</Action>
        <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:713f84a8-2dbf-44a7-936c-d445bfb890df</MessageID>
    </S:Header>
    <S:Body>
        <SOAP-ENV:Fault xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
            <faultcode xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
                       xmlns:ns0="http://www.w3.org/2005/08/addressing" xmlns="">ns0:InvalidCardinality</faultcode>
            <faultstring xmlns="">A header representing a Message Addressing Property 
                is not valid and the message cannot be processed</faultstring>
        </SOAP-ENV:Fault>
    </S:Body>
</S:Envelope>
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:
dispatch.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY,true);
dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY,"http://example/HelloWorld/sayHelloWorldFromRequest");
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 - "". 


Float to double conversion in Java

The first thing what we learn about representation of real number in computer systems is that it is only an approximation. Some of the real numbers cannot be expressed as floating point number (see this converter). How accurate our approximation is depends how many bytes we use to represent it. That is why you may be surprised about the result of the flowing code:
float fvar=0.1f;
double dvar=(double)fvar;
System.out.print("Are these number equal? ");
System.out.println(dvar==fvar);
System.out.println("Really?");
System.out.println("Float: "+fvar);
System.out.println("Double: "+dvar);
Are these number equal? true
Really?
Float: 0.1
Double: 0.10000000149011612
So if you need to convert some floats number to double and you would like to be sure that you won't scare your customer by charging her $99.99999998989 (quite easy to miss the dot) you need to convert floats to doubles by other means.

There are a three popular ways of doing this:
float fvar=0.1f;

//1. Convert float variable to string and then parse it as a double 
Double.parseDouble(new Float(fvar).toString());

//2. Use FloatingDecimal class
new sun.misc.FloatingDecimal(fvar).doubleValue();

//3. Use BigDecimal class 
new BigDecimal(String.valueOf(fvar)).doubleValue();
All of them do the work. The question is which one is the fastest? I've used the Brent Boyer's microbenchmark to answer this question. Here is the result. As you can see the method uses the FloatingDecimal class is the quickest one.