Miniware logo
Articles / A JSR 172 tutorial

A JSR 172 tutorial

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.

The content of this page can be reproduced as long as the author and the source are mentioned. For questions please use the forum. Nikos Fotiou
Comments
(Post new comment)

2006-11-14 16:05:24 Jose wrote:
Hello from Spain. I-m new to this. I suppose I have to call invoke in a separate thread, but how do I get error codes from server or establishing connection... any kind of error? How about cancelling a request to server (invoke) when it is still running? The idea is to show a screen with progress indicator while waiting response from server. Thanks