Tuesday 24 December 2013

Creating a Client Proxy in Java

@WebServiceClient(name = "CalculatorWSService", 
targetNamespace = "http://calculator.me.org/", 
wsdlLocation = "http://localhost:4933/CalculatorApp/CalculatorWSService?wsdl")
public class CalculatorWSService extends Service { //...
@WebEndpoint(name = "CalculatorWSPort")
public CalculatorWS getCalculatorWSPort() {
return super.getPort(new QName("http://calculator.me.org/", 
"CalculatorWSPort"), CalculatorWS.class);
}

Here  the  getCalculatorWSPort method  returns  an  object  that  implements  the
CalculatorWSinterface, which is discussed next. The no-arg  getPortmethod can be
used  in  general;  the  second  getPort method  accepts  a  variable-length  array  of
javax.xml.ws.WebServiceFeatureobjects that can be used by clients to configure certain
aspects of the invocation, such as whether to enable MTOM or WS-Addressing.


In the calculator example, the port has one method, to match the single addoperation
defined in the WSDL. Let’s take a step back and unpack this for a moment, as there’s
a lot going on here:
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "add", 
targetNamespace = "http://calculator.me.org/", 
className = "org.me.calculator.Add")
@ResponseWrapper(localName = "addResponse", 
targetNamespace = "http://calculator.me.org/", 
className = "org.me.calculator.AddResponse")
public int add(
@WebParam(name = "i", targetNamespace = "")
int i,
@WebParam(name = "j", targetNamespace = "")
int j);

As you can see, your seemingly simple, innocuous addmethod suddenly has a variety

of annotations adorning it. We’ll account for these one by one.
First, your WSDL specifies the following in the messages section:
<message name="add"> <part name="parameters" element="tns:add"></part> </message>
So the SEI needs to account for this message, and creates an annotation indicating that
the runtime will create a message with a QNamethat contains a local part of add, in the
specified  namespace. That message is derived from the Java class that is also generated,
org.me.calculator.Add, which looks like this:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "add", propOrder = {
"i",
"j"
})
public class Add {
protected int i;
protected int j;
//getters and setters omitted
That class acts as the wrapper for each of the integers that will be sent in the request.
The @Xml annotations on this class come from JAXB. They indicate how JAXB should
marshal and unmarshal instances of this class to and from XML. The @XmlTypeannotation is used to specify that this  Addclass matches a top-level complex type (or an
enum) within an XML schema, and the “name” property is specified as “add” in it, to
match the item’s name within the schema. If you look at the schema that your WSDL
refers to, you see the following complex type, which matches your Addclass:
<xs:complexType name="add">
<xs:sequence>
<xs:element name="i" type="xs:int"></xs:element>
<xs:element name="j" type="xs:int"></xs:element>
</xs:sequence>
</xs:complexType>
But why does this type get created for you? Integers are defined as basic types provided
with XML Schema; they are not custom types that you have written that require something special. The complex type that wraps these two integers is created in order to
match your WSDL, which uses the document/literal style. Here is the portion of the
WSDL that tells you this:
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" 
style="document"></soap:binding>
<operation name="add">
<soap:operation soapAction=""></soap:operation>
<input>
<soap:body use="literal"></soap:body>
</input>
Had you been using RPC and not document, the values would have been passed separately to the operation invocation just like method parameters.

The  @RequestWrapperand  @ResponseWrapperannotations capture information that JAXB
needs to perform the marshaling and unmarshaling operations. If your service is defined
as using document/literal mode, as ours is, this annotation also serves to resolve overloading conflicts.
Now, let’s write a quick program to invoke the generated client code and get a result
from your service. Here are the steps in their simplest form, stripped of any unnecessary
items so you can get the clearest picture.

import org.me.calculator.*;

public class CalculatorInvoker {
public static void main(String... arg) {
CalculatorWSService service = new CalculatorWSService();
CalculatorWS port = service.getCalculatorWSPort();
int result = port.add(2, 3);
System.out.println("Result: " + result);
}
}

Then you can run it:
>java -cp . CalculatorInvoker
Result: 5



No comments:

Post a Comment