|
The JSR 172 API is an API for accessing web services from mobile phones.
The usual procedure in order to achieve that is to use an automate tool
such as Sun wireless toolkit which produces a stub for us. But if we
want to access the API directly? In this artcile I present how
this can be achieved. I have created a simple service named Hello
and its url to my local apache-tomcat web server is: http://localhost:8080/axis/services/Hello.
The service receives as input a string and it returns "Hello " + the
input String. The wsdl file of the service can be viewed by typing the
following url: http://localhost:8080/axis/services/Hello?wsdl. The
important part of the wsdl file that we are going to use for our client
development are the following:
The variables:
<schema elementFormDefault="qualified" targetNamespace="http://localhost:8080/axis/services/Hello">
<element name="in0" type="xsd:string"/>
<element name="echoReturn" type="xsd:string"/>
</schema> |
So we see here that we have two variables. The in0 witch is a String and the
echoReturn which is a String also.
The other important part of the wsdl is the message part. In my case I have:
<wsdl:message name="echoResponse">
<wsdl:part element="impl:echoReturn" name="echoReturn"/>
</wsdl:message>
<wsdl:message name="echoRequest">
<wsdl:part element="impl:in0" name="in0"/>
</wsdl:message> |
So there are two messages. One is the echoRequest which has as a parameter
the echoReturn variable and the other is the echoRequest which has as
parameter the in0 variable Finally there is the following operation:
<wsdl:operation name="echo" parameterOrder="in0">
<wsdl:input message="impl:echoRequest" name="echoRequest"/>
<wsdl:output message="impl:echoResponse" name="echoResponse"/>
</wsdl:operation> |
The operation part tells us that in order to invoke the echo operation we
have to send an echoRequest message and we are going to get as response an
echoResponse message. This is all the necessary information we need to
invoke the service. A midlet that is going to access the service needs to be
crated. The first step is to import all the necessary libraries. These are
the following:
javax.microedition.midlet.*;
javax.microedition.lcdui.*;
javax.xml.rpc.JAXRPCException;
javax.xml.namespace.QName;
javax.microedition.xml.rpc.*;
javax.microedition.midlet.*;
javax.microedition.lcdui.*;
javax.xml.rpc.JAXRPCException;
javax.xml.namespace.QName;
javax.microedition.xml.rpc.*;
The next step is to create the java variables that are going to represent
the WSDL elements. According to JSR 172 "for each element object referenced
in the WSDL an Equivalent Element object is created" . The Element
constructor has two input parameters, a Qname that is consisted of the
target namespace and the name of the element and the name of the element,
and the type of the the element. In order to represent the type the Type
class is used. For example this is the code to create the in0 element:
Element in0 = new Element(new
QName("http://localhost:8080/axis/services/Hello","in0"), Type.STRING);
After creating the elements its time to implement the operation. In order to
make that we use the Operation class. More precisely in order to create a
new operation we use the static method "newIstance" of the Operation Class.
The newInstance method accepts three parameters, a QName that consists of
the target namespace and the operation name, the input element and the
output element. For example the code to implement the echo operation
follows:
Operation operation = Operation.newInstance(new
QName("http://localhost:8080/axis/services/Hello","echo"),in0,echoReturn);
Where echoReturn is a variable type Element just as in0.
After creating the operation the ENDPOINT_ADDRESS_PROPERTY property must me
set. In order to achive thet we use the ENDPOINT_ADDRESS_PROPERTY of the
Stub interface. For example in the following code the
ENDPOINT_ADDRESS_PROPERTY is set for the above Operation.
operation.setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,"http://localhost:8080/axis/services/Hello");
Now its time to invoke the operation.An operation is invoked by calling the
invoke method of the operations which returns an Object. By casting to the
appropriate type we have the result. The invoke method must be enclosed in a
try catch block as a JAXRPCException may occur. The following code invokes
the above mentioned operation:
String response ="";
try{
Object result =
operation.invoke("nikos");
response = (String)result;
}catch(JAXRPCException e){
//handle error
}
The complete code follows
import
javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.microedition.xml.rpc.*;
public class WSClientmidlet extends MIDlet {
private Operation operation;
private int array_size;
private Element in0;
private Element echoReturn;
private Element say_helloReturn;
public void startApp() {
in0 = new Element(new
QName("http://localhost:8080/axis/services/Hello","in0"),
Type.STRING);
echoReturn = new Element(new
QName("http://localhost:8080/axis/services/Hello","echoReturn"),
Type.STRING);
say_helloReturn = new Element(new
QName("http://localhost:8080/axis/services/Hello","say_helloReturn"),
Type.STRING);
operation = Operation.newInstance(new
QName("http://localhost:8080/axis/services/Hello","echo"),in0,echoReturn);
operation.setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,"http://localhost:8080/axis/services/Hello");
String response ="";
try{
Object result = operation.invoke("nikos");
response = (String)result;
}catch(JAXRPCException e){
//handle exception
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
} |
Lets create now a client for a more complex service. This service has an
operation, named return_employers, which accepts as input parameter an
array of integers named in0 and returns an array of complex type Name,
named return_employerReturn. The complex type Name consists of two
fields, a string named name and another string named surname. The parts
of the WSDL file showing the above information follows.
<element
name="in0">
<complexType>
<sequence>
<element maxOccurs="unbounded" minOccurs="0" name="item"
type="xsd:int"/>
</sequence>
</complexType>
</element>
<complexType name="Name">
<sequence>
<element name="name" nillable="true" type="xsd:string"/>
<element name="surname" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="return_employerReturn">
<complexType>
<sequence>
<element maxOccurs="unbounded" minOccurs="0" name="item"
type="impl:Name"/>
</sequence>
</complexType>
</element>
[...]
<wsdl:message name="return_employerRequest">
<wsdl:part element="impl:in0" name="in0"/>
</wsdl:message>
<wsdl:message name="return_employerResponse">
<wsdl:part element="impl:return_employerReturn"
name="return_employerReturn"/>
</wsdl:message>
[...]
<wsdl:operation name="return_employer" parameterOrder="in0">
<wsdl:input message="impl:return_employerRequest"
name="return_employerRequest"/>
<wsdl:output message="impl:return_employerResponse"
name="return_employerResponse"/>
</wsdl:operation>
|
Firstly lets implement the array of integers. We need to import the same
packages as the simple example. As we see in the WSDL file the array is
represented as a complex type which contains one element with the
attributes maxOccurs and minOccurs set. The Java code is the following:
ComplexType item = new
ComplexType();
item.elements = new Element[1];
item.elements[0] = new Element(new
QName("http://localhost:8080/axis/services/WSComplex","item"),
Type.INT,0,Element.UNBOUNDED,false);
Element in0 = new Element(new
QName("http://localhost:8080/axis/services/WSComplex","in0"),item);
The fields that we are using in the constructor for item.elements[0] are
the qname, minOcuurs, maxOccurs and nillable. Now we are going to
implement the complex type name as well as the return_employREsponse
which is an array of name.
ComplexType name = new
ComplexType();
name.elements = new Element[2];
name.elements[0] = new Element(new
QName("http://localhost:8080/axis/services/WSComplex","name"),Type.STRING);
name.elements[1] = new Element(new
QName("http://localhost:8080/axis/services/WSComplex","surname"),Type.STRING);
ComplexType item2 = new
ComplexType();
item2.elements = new Element[1];
item2.elements[0] = new
Element(new
QName("http://localhost:8080/axis/services/WSComplex","item"),name,0,Element.UNBOUNDED,false);
Element return_employerReturn = new Element(new
QName("http://localhost:8080/axis/services/WSComplex","return_employerReturn"),item2);
Now its time to implement the operation and to invoke it. According to JSR
172 if the input parameter of a service invocation is an array, it must be
stored in the first element of another array. This can become clearer by
viewing the code:
Operation operation =
Operation.newInstance(new
QName("http://localhost:8080/axis/services/WSComplex","return_employer")
,in0,return_employerReturn);
operation.setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,"http://localhost:8080/axis/services/WSComplex");
Object[] ob = new Object[]{new Integer(4),new Integer(5),new Integer(6)};
Object[] obarray = new Object[]{ob};
try{
Object result =
operation.invoke(obarray);
}catch(JAXRPCException e){
}
The result is encoded in a similar way. We must create a class named
name, in the same package as the midlet, to hold the name complex type and
the rest procedure is similar to the preparation we made to invoke the
operation:
public class name {
public String name;
public String surname;
}
//In the midlet we add the following
code:
Object result =
operation.invoke(obarray);
Object[] response = (Object[])((Object[])result)[0];
name[] Name = new name[response.length];
for (int x = 0; x < response.length ; x++){
Name[x] = new name();
Name[x].name = (String)(((Object[])response[x])[0]);
Name[x].surname = (String)(((Object[])response[x])[1]);
}
So as you can see the result is stored in the following structure:
object[] {object[] {object[]{name, surname},object[]{name, surname} ...}}
The complete code follows:
 
package
hmwscomplex;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.microedition.xml.rpc.*;
public class hmWScomplex extends MIDlet {
public void startApp() {
ComplexType item = new ComplexType();
item.elements = new Element[1];
item.elements[0] = new Element(new QName("http://localhost:8080/axis/services/WSComplex","item"),
Type.INT,0,Element.UNBOUNDED,false);
Element in0 = new Element(new QName("http://localhost:8080/axis/services/WSComplex","in0"),item);
ComplexType name = new ComplexType();
name.elements = new Element[2];
name.elements[0] = new Element(new QName("http://localhost:8080/axis/services/WSComplex","name"),Type.STRING);
name.elements[1] = new Element(new QName("http://localhost:8080/axis/services/WSComplex","surname"),Type.STRING);
ComplexType item2 = new ComplexType();
item2.elements = new Element[1];
item2.elements[0] = new Element(new QName("http://localhost:8080/axis/services/WSComplex","item"),name,0,Element.UNBOUNDED,false);
Element return_employerReturn = new Element(new QName("http://localhost:8080/axis/services/WSComplex","return_employerReturn"),item2);
Operation operation = Operation.newInstance(new QName("http://localhost:8080/axis/services/WSComplex","return_employer"),in0,return_employerReturn);
operation.setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,"http://localhost:8080/axis/services/WSComplex");
Object[] ob = new Object[]{new Integer(4),new Integer(5),new
Integer(6)};
Object[] obarray = new Object[]{ob};
try{
Object result = operation.invoke(obarray);
Object[] response = (Object[])((Object[])result)[0];
name[] Name = new name[response.length];
for (int x = 0; x < response.length ; x++){
Name[x] = new name();
Name[x].name = (String)(((Object[])response[x])[0]);
Name[x].surname = (String)(((Object[])response[x])[1]);
}
}catch(JAXRPCException e){
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
} |
I strongly suggest reading the Part 8 of the JSR 172 API specification as
well as the Appendix A, which has a complete example.
|