Web Services with Java 2 Micro Edition

by
Mark Balbes, Principal Software Engineer
Object Computing, Inc. (OCI)

Introduction

Web services have become all the rage. With the promise to ease interoperability and allow for large scale software collaboration over the internet by offering services through a language and platform independent XML-based interface. Web Services were discussed in a previous SETT article, "SOAP and Web Services" by Mark Volkmann

Of course, we don't want our J2ME applications to be left off the bandwagon. To our rescue comes JSR 172, the J2ME Web Services specification.

Actually, the JSR includes two different and independent APIs, either or both of which can be available. The first API adds support for XML parsing that is a subset of JAXP functionality. XML parsing in J2ME will be the subject of a future Java News Brief. Our current interest is focused on web services.

Web Services

The promise of interoperability makes web services a good fit with J2ME. The JSR 172 specification, which was released on March 3, 2004, defines how a J2ME-enabled device can become a web services client. There is currently no specification for making a J2ME web service provider.

Web services in J2ME have been available up until now only in 3rd party libraries like kSOAP. However, this means that the library must be shipped with the application, bloating the deployment footprint and runtime size of the application.

With the J2ME Web Services API, web service support is built into the device rather than having to be shipped with the application. The advantages are obvious: faster deployment, smaller footprint, and an implementation that is optimized for the device and wireless network. The disadvantage is that if your device doesn't provide support then you are out of luck.

If you are interested in all of the technical details of the API, then read the JSR. For the most part, it points to other XML and web service specifications, indicating which features are or are not supported. Here are the highlights in a nutshell:

XML Encoding

It is interesting to see in the specification that XML encoding on the device is not required. What is required is that the device be able to consume XML web services from the internet. However, the XML messages can be transformed into another format before reaching the device. Conversely, the device can invoke web services using non-XML encodings as long as they are transformed to XML before reaching the web server. In other words, devices running on proprietary networks can use a more efficient format for the web service messages as long as it is transparent to the web service consumer and provider.

Portability and Interoperability

Standard web services promote interoperability through the use of WSDL. Web Service clients are created by tools that read the WSDL file that describes a web service. These tools then generate stubs that can be used by the client to access the web service as if it was a local object. The generated stubs are specific to the tool that generates them. For a J2SE web service, this isn't much of a problem. It just means that you must deploy a portion of the web service toolkit used to build the stubs with the web service client. Portability is provided, as usual, by the Java VM.

The J2SE deployment model does not work for J2ME, however. Web service support is provided as part of the J2ME platform on the device. If the support is not consistent across devices, there would be no portability. It would defeat the purpose of J2ME Web Service if the deployed application was bloated because it's deployment had to include a portion of the web services toolkit.

To avoid this problem, JSR 172 goes a step further than the standard web services specifications. It also requires that each device also provide a JAX-RPC Service Provider Interface (SPI) as part of the runtime environment. Generated stubs must be written to the SPI to ensure portability. In general, J2ME developers do not have to worry about the SPI. It is used by the stub generator tool. Once the stub has been generated, the J2ME developer writes to the stub, not the SPI.

An Example

Now that we've looked at the capabilities of J2ME Web Services, let's take a look at a simple example. For fun, I have written a web service in C# on the .NET platform. I have two reasons for doing this. First, it shows the power of interoperability with web services. Second, it is easier to write a web service for J2ME using .NET than using Axis, one of the major Java-based web service toolkits. Now, before everyone screams about how easy Axis is to use (and it is), .NET has one advantage over Axis. The default message format for .NET web services is document/literal. The default message format (for now) for Axis is RPC/encoded. Remember that J2ME Web Services only supports the document/literal message format and you'll understand why using .NET to generate this example is slightly easier.

It might be interesting to note that my original intention for this article was to use the Google web service for the example. It would be quite fun to create an application that lets you search Google from a J2ME application. Unfortunately, although it is quite easy to use the Google web service from J2SE, it is impossible to use it from J2ME because Google has chosen to use rpc/encoded formatting for their SOAP messages.

Instead of using Google, we'll create our own simple web service to track a comic book collection. (Those that know me, know why.) In fact, we'll make it very simple. The client can invoke the web service to find out what comic book titles are in the collection. We will then display these titles on the device. After selecting a title, the application will display the issues in the collection and their associated conditions.

The Web Service

The C# code for the server is in a file called ComicServiceJ2ME.asmx and deployed into IIS at \Inetpub\wwwroot\ComicServiceCF. You can see the full source code here. Most of the file contains boilerplate code. We are interested in the two web methods listed below.

        [WebMethod]
        public string[] GetTitles(int ownerID) {
            return new string[] {"Superman", "Batman", "Green Lantern", "The Flash"};
        }

        [WebMethod]
        public string[] GetConditions(string title) {
            if (title == "Superman") return new string[] {"Poor", "Mint", "Good"};
            else if (title == "Batman") return new string[] {"Good", "Good", "Fair", "Poor"};
            else if (title == "Green Lantern") return new string[] {"Fair"};
            else if (title == "The Flash") return new string[] {"Mint", "Good", "Good", "Fair"};
            else return new string[0];
        }
        

The GetTitles(int ownerID) method returns an array of strings corresponding to the comic book titles in the collection associated with the ownerID. The GetConditions(string title) method returns the condition of each comic book of that title that is in the collection. Of course, in a real application, these would be extracted from some persistent storage like a database or XML file.

MS IIS will automatically generate a WSDL file once the above code is deployed. The WSDL can be viewed by appending ?wsdl to the end of the URL that points to the web service. We will need to access the WSDL in order to generate stubs.

The Web Service J2ME Stub

We generate the stubs by using the J2ME Wireless Toolkit 2.2. See the figure below. We simply specify the location of the WSDL (as either a file location or URL), the output path, and the output package. Because J2ME Web Services clients are supported on both CLDC 1.0 and 1.1, we must specify which platform to target. Targeting CLDC 1.0 will provide the best portability but will be less efficient if floats are being returned. Under CLDC 1.0, floats are not supported so they will be returned as strings. CLDC 1.1 supports floating point types and operations. For our web service, it doesn't matter since we are only passing ints and strings. J2ME Wireless Toolkit 2.2

The toolkit generates 7 .java files and their associated .class files although only the Stub class itself is needed by the J2ME application developer. The rest are support classes used by the stub implementation.

File Description
ComicServiceJ2MESoap.java Interface implemented by the stub.
ComicServiceJ2MESoap_Stub.java Generated stub.
GetTitles.java Wrapper object for the GetTitles() method parameters.
GetTitlesResponse.java Wrapper object for the GetTitles() return value.
GetConditions.java Wrapper object for the GetConditions() method parameters.
GetConditionsResponse.java Wrapper object for the GetConditions() return value.
ArrayOfString.java Wrapper for a String[]

Invoking the web service is as easy as calling a method on the stub class. The basic steps are:

Note that the thread will block while it waits for a return value. J2ME Web Services do not support asynchronous operations. Therefore, it is usually best to invoke the web service from a separate thread. In fact, the J2ME Wireless Toolkit emulator will give a warning message if the web service is invoked from an event callback thread. Below is an example of how to call our web service.

            ComicServiceJ2MESoap_Stub service = new ComicServiceJ2MESoap_Stub();
            service._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, webServiceURL);
            service._setProperty(Stub.SESSION_MAINTAIN_PROPERTY, Boolean.FALSE);
            ArrayOfString titlesArray = service.getTitles(userid);
            String[] titles = titlesArray.getString();
        

The MIDlet

The J2ME MIDlet in this example is very simple. (See the images below.) The code can be viewed here. It consists of 3 custom screens and one screen displayed by the device to notify the user that air time is being requested.

Device requests permission to access the network. The application tells the user it is loading the data.
A list of available comic book titles. A list of issues for a given title and their associated conditions.

When the MIDlet is started, it loads the comic book titles from the web service. While it is loading, it displays a message to indicate this. Once the titles are loaded, they are displayed as buttons. Note that we are using the new features of MIDP 2.0 to display clickable buttons on the screen and to give a more sophisticated layout than was possible in MIDP 1.0. The code below shows how to do this.

    StringItem item = new StringItem(null, titles[i], Item.BUTTON);
    form.append(item);
    item.setLayout(Item.LAYOUT_2 | Item.LAYOUT_NEWLINE_AFTER);
    item.addCommand(issuesCommand);
    item.setItemCommandListener(this);
        

In the code snippet above, we create a StringItem and tell it to display in a BUTTON style. We also set the layout for the button, telling it to use the new MIDP 2 layout style and that we want a new line after it. Finally, we use another new feature of MIDP 2 that allows us to associate a command and a command listener with an individual item. In a J2SE application, I would normally make the ItemCommandListener an anonymous inner class, one for each button. However, with J2ME, every class we create takes up precious resources. Instead, I have the MIDlet itself listen to all buttons and then sort out which button was invoked in the callback method. It's not as pretty but is much more efficient.

When we call the web service, we always do it from a new Thread. This prevents the event thread from being blocked by the synchronous call to the web service. Once the thread has retrieved the result, we then update the display. Fortunately, the lcdui GUI toolkit for MIDP is thread safe, unlike Swing. So it is perfectly safe for us to update the display from our thread. You'll notice that the MIDlet itself implements the Runnable interface and is used by the new Thread object to invoke the web service. Again, this is done for efficiency whereas, in J2SE, I would create a couple of Runnable anonymous inner classes, understanding that I was trading off a little disk space (for the class definition) and performance (because the anonymous inner classes must be loaded) in favor of code readability.

Summary

In this article, we have seen how easy it is to use J2ME Web Services. Before using this feature, though, you must answer two questions.

If the answer to either of these questions is no, then you want to avoid using J2ME web services and instead use standard MIDP 1.0 HTTP capabilities. (See my previous article on MIDP features.) However, if you answer yes to both, then you can use J2ME web services to easily create interoperable and portable J2ME applications.

References


Valid XHTML 1.0 Strict [Valid RSS]
RSS
Top