HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

The new kid on the Internet technology block is Web services and its implementation technology, SOAP. Simple Object Access Protocol is an XML vocabulary used to describe messaging and remote procedure calls between distributed components.

The power of SOAP is its flexibility, which comes from SOAP's foundation in XML, and its ubiquity, which comes from the use of HTTP as a primary transport mechanism.

Any modern language that works with the Internet is likely to support both XML and HTTP, and Java is no exception. There are, in fact, many good toolkits for using SOAP with Java and Apache Axis is one of the most popular. Choices abound for the developer working with J2SE or J2EE.

What About the J2ME Developer?
In the world of mobile devices, a world still constrained by hardware and network limitations, the concepts behind distributed computing are slowly taking root. Why cram all the functionality of a stock ticker or m-commerce application into one little MIDlet when you could offload much of the functionality to a server with more resources? Not to mention the flexibility it provides for system and database administrators. How often do they wish they could fix simple problems by logging in from a Palm Pilot or BlackBerry?

SOAP provides distributed functionality in a manner RMI and CORBA cannot. It's language agnostic, so a MIDlet can converse with a legacy COBOL application wrapped within a PHP SOAP service. It's also lightweight; there's no need for the resource-hungry ORB platform to support it. One small toolkit, kSOAP, weighing in at a whopping 41KB, is all that's needed to bring wireless Web services to your MIDlets.

Application Architecture
The Web service I'll be looking at is a simple example of what can be accomplished. The taxCalc service takes two parameters, a tax rate and a subtotal, and calculates a total for a given purchase.

The service is written in PHP using the NuSOAP toolkit, a simple and easy way to prototype a Web service. In a production environment, once I had the definition of the service API frozen, I would then deploy a more robust version in a J2EE environment using Axis. For the purposes of this article, however, the use of PHP is handy to illustrate just how flexible and language-neutral SOAP can be.

The power of using a MIDlet is that it's deployable across a wide range of mobile devices, from a Motorola handset to a Palm Pilot or RIM BlackBerry. This provides the same SOAP client with a wide range of platforms for its deployment, and opens the door for a greater use of the Web service.

As can be seen in Figure 1, the architecture of our Web service is quite simple. On the server side we have an Apache Web server hosting the PHP service, which exports one interface function: taxCalc(). The endpoint for this service is available via www.whytewolf.ca/ws/taxCalc.php.

Figure 1

On the client side we have our J2ME client device running a MIDlet, which uses the kSOAP library to access the SOAP service. Messaging is done through the HTTP protocol, which is supported by all MIDP-compliant devices.

While you might think that a more complex Web service would require a more complex architecture, that's not necessarily the case. Any given application that uses Web services would only need to access a separate node for each service it consumes. If the same server or application provides multiple services ­ especially if multiple functions are exported by the same service ­ the application architecture is greatly simplified.

Ultimately, no matter which services are used or how many are available to the client, SOAP is about XML messages and the data they contain. It is these messages that the entire SOAP protocol is built upon.

SOAP Messages
As stated earlier, SOAP messages are simply a specific XML vocabulary used to encapsulate document or procedure calls. As can be seen from Listing 1 , the root element of any SOAP message is the envelope tag. Inside this envelope the request and parameters are transmitted to the server and the response is transmitted back.

Note the use of XML Schema data types to encode the data that's being sent as parameters. In Listing 1 the function call to taxCalc is made using a taxCalc element to enclose the two parameters, rate and sub. Each parameter element is given a type via the xsi:type attribute. The xsd:string type is an XML Schema string that, once the server receives it, is dynamically cast via PHP's internal mechanism to a numeric value.

The request is transmitted as an HTTP POST request and sends a special header: SOAPAction. This header indicates the specific service and function being called by including that information as a URI. In the case of taxCalc, what is used is a URN that indicates the service and function. This information is often useful, but the SOAP spec doesn't require that the server use it. The same URN is also used as a namespace URI for the service request element taxCalc.

Looking at the response envelope in Listing 2 we see that again the data being transmitted back to the client is wrapped within an envelope element's body tag and encoded using XSD data types. The response element taxCalcResponse wraps the actual value transmitted, in this case stored in a noname element of type xsd:float, a floating-point number.

What if there's a problem? How does SOAP indicate a service failure and not leave the client hanging? The SOAP spec calls for the use of a Fault framework to indicate exceptional conditions in the execution of a service.

Listing 3 shows the anatomy of a SOAP fault. Like the request and response, a fault is transmitted using a SOAP envelope element, but instead of providing RPC calls or responses, the envelope's body contains the fault information. Four fault tags are transmitted: faultcode, faultstring, faultactor, and faultdetail. Faultcode and faultstring are mandatory. These two describe what type of fault occurred ­ if it was bad data from the client, a server failure, a version mismatch, or a miscommunication ­ and provide a message explaining the problem. As can be seen in Listing 3, the faultcode is client, meaning the client sent invalid data. The faultstring explains: taxCalc() can accept only nonzero subtotal values. The other two fault tags, faultactor and faultdetail, are optional and provide extended information on the cause of failure.

As can be seen in the examples, the SOAP 1.1 specification provides a highly detailed and flexible way to transmit data in both directions as well as indicate problems with the execution. However, with that flexibility comes a price: complexity. Parsing out the required details and marshaling the required data can be quite a task. Luckily for MIDlet developers, the kSOAP toolkit simplifies matters greatly.

For more information on SOAP, check out the Resources section at the end of this article.

The kSOAP Toolkit
The kSOAP project is based on kXML, a lightweight pull parser designed specifically for use with MIDP. Enhydra, a provider of J2ME and J2EE solutions, hosts both kSOAP and kXML. Both toolkits are available through the Enhydra Public License, and come packaged in a single JAR for use in a SOAP environment ­ handy if the MIDlet needs to parse additional, non-SOAP­related XML.

One of kSOAP's biggest strengths is its relative simplicity. Most enterprise-level SOAP toolkits often rely on the use of a Web Services Description Language (WSDL) generated proxy object to make function calls. This is useful if a service has a published description and when the toolkit requires the instantiation of several different client and transport objects. While kSOAP doesn't support WSDL, it does make calling a service relatively painless. Only two objects are required: the SOAPObject and Http-Transport.

In addition, kSOAP makes it very easy to capture fault data. The toolkit maps all SOAP faults to an exception object known as SoapFault. In this manner, a Fault can be caught and handled like any other exception in the MIDlet.

The development environment used for this article is Sun's J2ME Wireless Toolkit. To build an application using kSOAP, first download the ksoap-midp.jar file from ksoap.enhydra.org. Place this JAR in the application's lib/ directory. The WTK will add all JARs in lib/ to the classpath.

Import all the necessary classes by inserting the following statements:

import org.ksoap.*;
import org.ksoap.transport.*;

Now your MIDlet is ready to use kSOAP.

The taxCalcClient MIDlet
Up to now I've been laying the foundation for developing the taxCalcClient MIDlet. Now I'll dissect the construction of this application. While only specific relevant portions of the source code will be explored, you can download the complete application with source code from below.

As shown in Figure 2, the first screen the user interacts with is a TextBox requesting the amount of the purchase. This is the subtotal value that will be passed to the taxCalc() function on the SOAP server. Looking at the MIDlet source we see the private variable rate has been set to +7%. This is the tax percentage that will be passed to the taxCalc() function as the rate parameter.

Figure 2

The Calculate event is fired when the right soft key is pressed. This calls the getTax() function (see Listing 4), which provides MIDlet's SOAP functionality. The heart of this function is:

try {
SoapObject client = new
SoapObject("urn:soap-whytewolf-ca:
taxcalc","taxCalc");
client.addProperty("rate",rate);
client.addProperty("sub",amount);
HttpTransport ht = new
HttpTransport("http://www.whytewolf
.ca/ws/taxCalc.php",
"urn:soap-whytewolf-ca:taxcalc#taxCalc");

taxMsg.setText("$" + ht.call(client));
}

To prepare our client to use a SOAP server we create a new SoapObject, passing the constructor the Namespace URI for the SOAP call and the name of the function being called. This SoapObject also needs to be prepared with the parameters the function accepts. Recall our taxCalc function took two strings: rate and sub. Each of these properties and the values to be passed through them are added to the client object using the addProperty method, which takes the name of the property and the value of the property as parameters.

While these are provided as name-value pairs, some SOAP servers don't check the property name and instead use the values in the order they were passed. For this reason I suggest adding properties in the order the function would expect the parameters, in this case, rate first and then sub (see Figure 1 for the function's signature).

We now create a new HttpTransport object, which will provide the needed functionality to actually call the SOAP service. We pass the constructor the endpoint URL for the service and the SOAPAction URI we'll be calling the service with. To execute the service, call it using the call() method, passing call() the SoapObject that will invoke the service. Call() returns whatever value is returned from the SOAP service, in this case, a float that we cast to a string and use in the StringItem of our second screen to display the results of the tax calculation. Figure 3 shows the resulting screen, assuming all goes well.

Figure 3

As has already been indicated, the taxCalc SOAP service will not accept negative or zero-valued rates or subtotals. Should a client send anything other than a nonzero value for the rate or subparameters, the service will return a SOAP fault.

The first catch clause in getTax() will catch any SOAP fault exception the call() method throws. As seen in the following code, retrieving the SOAP fault information is quite simple:

catch (SoapFault sf){
taxMsg.setLabel("FAULT:\n");
String faultString = "Code: " + sf.faultcode + "\nString: " +
sf.faultstring;
taxMsg.setText(faultString);

}

The SOAP fault is stored inside an exception object known as SoapFault (quite a convenient name). Any of the four fault fields can be retrieved through their named properties, sf.faultcode, sf.faultdetail, sf.faultactor, or sf.fault string. As shown in the example, it's quite easy to output these into the StringItem to indicate to the user a fault has occurred (see Figure 4).

Figure 4

Having handled the fault, the MIDlet allows the user to go back to the first screen and correct the data.

Conclusion
SOAP can be a very complex realm to explore, especially the XML mechanisms used to transfer data between disparate systems, languages, and toolkits. The wireless world of J2ME and MIDlets requires tools of small stature and great power. Luckily when it comes to SOAP, the kSOAP toolkit provides not only small size and great functionality, but also relative simplicity and ease of use for the developer. Using kSOAP, a J2ME developer can develop complex SOAP Web services clients in a remarkably short time.

Resources

  • SOAP 1.1W3C Note: www.w3.org/TR/SOAP
  • kSOAP toolkit: http://ksoap.enhydra.org
  • Java 2 Platform, Micro Edition: http://java.sun.com/products/j2me
  • Mobile Information Device Profile: http://java.sun.com/products/midp
  • Sun J2ME Wireless Toolkit: http://java.sun.com/products/j2mewtoolkit
  • Gupta, S. "Combining RMI with SOAP." Java Developer's Journal. SYS-CON Media. Vol. 7, issue 6.
  • NuSOAP PHP SOAP toolkit: http://dietrich.ganx4.com/nusoap/index.php
  • Apache Axis: http://xml.apache.org/axis

    Author Bio
    S.D. Campbell is a senior consultant with ContractBank (www.contractbank. com/CWIC) and heads the Web development program at Applied Multimedia Training Centers in Calgary, Alberta. His consulting company, Whyte.Wolf, helps small businesses move into wireless and Web services. [email protected]

    	
    
    
    
    Listing 1: SOAP request
    POST /ws/taxCalc.php HTTP/1.1
    SOAPAction: urn:soap-whytewolf-ca:taxcalc#taxCalc
    Content-Type: text/xml
    Content-Length: 557
    User-Agent: kSOAP/1.0
    Host: www.whytewolf.ca
    
    
    
    <SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
     <SOAP-ENV:Body SOAP ENV:encodingStyle="http:
                //schemas.xmlsoap.org/soap/encoding/">
     <taxCalc xmlns="urn:soap-whytewolf-ca:taxcalc" id="o0" SOAP-ENC:root="1">
       <rate xmlns="" xsi:type="xsd:string">7</rate>
       <sub xmlns="" xsi:type="xsd:string">856</sub>
      </taxCalc>
     </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    
    Listing 2: SOAP response
    HTTP/1.1 200 OK
    Date: Mon, 12 Aug 2002 01:31:10 GMT
    Server: Apache/1.3.14 (Unix) mod_perl/1.24 PHP/4.0.6 FrontPage/4.0.4.3
    mod_ssl/2.7.1 OpenSSL/0.9.6
    X-Powered-By: PHP/4.0.6
    Status: 200 OK
    Connection: Close
    Content-Length: 510
    Content-Type: text/xml; charset=UTF-8
    
    
    <?xml version="1.0"?>
    <SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:si="http://soapinterop.org/xsd">
    <SOAP-ENV:Body>
    <taxCalcResponse>
    <noname xsi:type="xsd:float">915.92</noname>
    </taxCalcResponse>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    
    Listing 3: SOAP fault
    HTTP/1.1 200 OK
    Date: Mon, 12 Aug 2002 01:32:12 GMT
    Server: Apache/1.3.14 (Unix) mod_perl/1.24 PHP/4.0.6 FrontPage/4.0.4.3
    mod_ssl/2.7.1 OpenSSL/0.9.6
    X-Powered-By: PHP/4.0.6
    Status: 500 Internal Server Error
    Connection: Close
    Content-Length: 607
    Content-Type: text/xml; charset=UTF-8
    
    
    <?xml version="1.0"?>
    <SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:si="http://soapinterop.org/xsd">
    <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
    <faultcode>Client</faultcode>
    <faultactor></faultactor>
    <faultstring>Must supply a non-zero subtotal.</faultstring>
    <faultdetail></faultdetail>
    </SOAP-ENV:Fault>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    
    Listing 4: getTax() function
    private void getTax() {
               amount = input.getString();
               try{
                    SoapObject client = new
    SoapObject("urn:soap-whytewolf-ca:taxcalc","taxCalc");
                    client.addProperty("rate",rate);
                    client.addProperty("sub",amount);
    
    
                    HttpTransport ht = new
    HttpTransport("http://www.whytewolf.ca/ws/taxCalc.php",
                       
    "urn:soap-whytewolf-ca:taxcalc#taxCalc");
    
    
                    taxMsg.setText("$" + ht.call(client));
                }
                catch (SoapFault sf){
                    taxMsg.setLabel("FAULT:\n");
                    String faultString = "Code: " + sf.faultcode + "\nString: "
    + sf.faultstring;
                    taxMsg.setText(faultString);
    
    
                } catch (Exception e) {
                    e.printStackTrace();
                    taxMsg.setLabel("ERROR:\n");
                    taxMsg.setText(e.toString());
                }
           getForm.addCommand(backCommand);
           getForm.setCommandListener(this);
           display.setCurrent(getForm);
           
        }
    
     
    

    Additional Code For This Article (~ 130 KB ~zip format )

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.
      E-mail: [email protected]

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.