|
By C. Enrique Ortiz
April 2004
Developed within the Java Community Process as JSR 172, the J2ME Web Services API (WSA) extends the Java 2 Platform, Micro Edition to support web services. The API's two optional packages standardize two areas of functionality that are crucial to clients of web services: remote service invocation and XML parsing. This article introduces these two packages, shows you how to use them, and describes the support for them in the J2ME Wireless Toolkit.
To learn more about the complexity of developing wireless applications in general, please see the article "The Complexity of Developing Mobile Networked Data Services, J2ME Wireless Connection Wizard For Sun ONE Studio," which describes the main elements of mobile networked applications.
Introduction
WSA is designed to work with J2ME profiles based on either the Connected Device Configuration (CDC) or the Connected Limited Device Configuration (CLDC 1.o or CLDC 1.1). The remote invocation API is based on a strict subset of J2SE's Java API for XML-Based RPC (JAX-RPC 1.1), with some Remote Method Invocation (RMI) classes included to satisfy JAX-RPC dependencies. The XML-parsing API is based on a strict subset of the Simple API for XML, version 2 (SAX2).
The goal of WSA is to integrate fundamental support for web services invocation and XML parsing into the device's runtime environment, so developers don't have to embed such functionality in each application -- an especially expensive proposition in resource-constrained devices like mobile phones and personal digital assistants.
Core Specifications
The core specifications and application-level protocols that define web services are promoted by the Web Services Interoperability Organization (WS-I), and governed by the World Wide Web Consortium (W3C) and the Organization for the Advancement of Structured Information Standards (OASIS). Four key standards specify how to create, deploy, find, and use web services:
 |
|
Defines transport and data encoding |
|
Defines how remote services are described |
|
Defines how remote services are discovered |
|
Defines the Extensible Markup Language (XML) and XML Schema |
These primary specifications tend to be very broad, and developers of web services have found it difficult to achieve full interoperability. To resolve differences in interpretations of the standards, WS-I has defined a set of conformance rules called the WS-I Basic Profile, version 1.0. JSR 172 conforms to the Basic Profile.
Web Services on the J2ME Platform
JSR 172 specifies standardized client-side technology to enable J2ME applications to consume remote services on typical web services architectures, as Figure 1 illustrates:
 |
| |
|
Figure 1. J2ME Web Services in a Typical Web Service Architecture
|
At a high level, this web service architecture has three elements:
- A network-aware application residing on a WSA-enabled wireless device. The application includes a JSR 172 stub that uses the JSR 172 runtime to communicate with the network. I'll describe soon the roles that the stub and the runtime play.
- The wireless network and the Internet, and the corresponding communication and data-encoding protocols, including binary protocols, HTTP, and SOAP/XML.
- A web server, acting as the service producer, typically behind one or more firewalls and a proxy gateway. The web server often provides access to back-end applications and servers on a private network.
The first version of WSA addresses only the consumption of web services. It does not support creation and deployment of service endpoints; a J2ME device can be a service consumer, but not a service producer. JSR 172 also does not specify an API for service discovery using UDDI.
Understanding JSR 172
To begin to understand how J2ME Web Services works, let's first look at how a typical JSR 172-based application is organized:
The application itself is a smart client based on the Mobile Information Device Profile (MIDP) or the Personal Basis Profile (PBP), with business-specific logic, user interface, persistence logic, and life-cycle and application-state management. To handle XML documents, the application can employ the JAXP subset API. To consume web services, it can use the JAX-RPC subset API, employing JSR 172 stubs and the runtime.
In devices such as cell phones, typically the application and the JSR 172 stub reside in the device's memory, while all the JSR 172 elements, along with the underlying profile and configuration, are embedded in the device itself.
The JSR 172 Runtime and Service Provider Interface
At the center of JSR 172 operations is the runtime, with its service provider interface, which enables the stubs to perform all the tasks associated with invoking an RPC service endpoint:
- Set properties specific to an RPC invocation
- Describe the RPC invocation input and return values
- Encode input values
- Invoke the RPC service endpoint
- Decode and return to the application any values that the service endpoint returns
Figure 3 depicts the relationships between the client application, the stub, and the runtime:
 |
| |
|
Figure 3. The JSR 172 Runtime and SPI
|
While the JSR 172 runtime hides complexities such as connection management and data encoding, the SPI decouples stubs from details of the runtime implementation, allowing portability of stubs between vendor?s implementations.
The client application doesn't interact with the runtime and SPI directly; it uses stubs instead. The runtime and SPI are mainly concerns of third-party vendors who intend to develop JSR 172 runtimes and automation tools, such as the WSDL-to-Java mapping tool that developers use to generate stubs.
Because application developers do not use the SPI directly, this article does not cover the runtime and SPI in more detail. You can consult the JSR 172 specification if you want more information on them.
The JSR 172 JAX-RPC Subset API
As I mentioned, the JSR 172 web services remote invocation API is based on a strict subset of J2SE's JAX-RPC 1.1 API. The following list describes this subset at a high level. For details, please see the JSR 172 specification:
- Conforms to the WS-I Basic Profile
- Supports SOAP 1.1
- Supports any transport, such as HTTP 1.1, that can deliver SOAP messages, and that has a defined protocol binding for SOAP 1.1
- Supports a full suite of data types: boolean, byte, short, int, long, float, double, String, arrays of primitive types, and complex types (structures containing primitive or complex types)
 |
 |
 |
 |
 |
Note that true floating-point support can occur only in software stacks based on CDC or on CLDC 1.1. On CLDC 1.0, float and double map to String. Also note that the supported data types restrict the WSDL data types supported. A later section will discuss WSDL-to-Java data type mapping.
|
 |
 |
 |
- Supports the Literal representation of a SOAP message representing an RPC call or response (a WSDL operation using the Document/Literal messaging mode); doesn't support Encoded representation
- Doesn't support SOAP Messages with Attachments.
- Doesn't support SOAP message handlers.
- Doesn't support service endpoints; that is, doesn't enable a device to be a web service producer.
- Doesn't service discovery support (UDDI).
- To reduce demands on network bandwidth -- and to save users time and per-byte charges ? doesn?t mandate the use of XML encoding on the device itself
 |
 |
 |
 |
 |
Note: Implementations may use a more efficient encoding, such as a binary protocol between the device and the wireless gateway, as long as it's transparent to both consumer and producer.
|
 |
 |
 |
The following table describes the packages in the JSR 172 JAX-RPC subset:
 |
javax.microedition.xml.rpc
|
Defines Type, used by stubs, which is an enumeration of valid simple types: boolean, byte, short, int, long, float, double, String |
Defines ComplexType, used by stubs, which is a special Type that represents WSDL xsd:complextype definitions |
Defines Element, used by stubs, which represents the WSDL xsd:element definitions |
Defines Operation, used by stubs, which represents a WSDL wsdl:operation for a service endpoint |
Defines FaultDetailHandler, an interface implemented by stubs that handle custom faults |
Defines FaultDetailException, used by stubs, an Exception class that represents an exception thrown by the runtime implementation as a result of a JAXRPCException; returns the service-specificDefines a QName (qualified name) class that is used by a stub exception detail values, and an associated QName, to a Stub instance |
javax.xml.namespace
|
Defines a QName (qualified name) class that is used by a stub |
javax.xml.rpc
|
Defines the Stub interface, which is the base interface for all JAX-RPC stubs; also defines NamespaceConstants, and the JAXRPCException-related classes |
java.rmi
|
A subset of the standard java.rmi package included to satisfy JAX-RPC dependencies; defines only the Remote interface, and three exception classes: MarshalException, RemoteException, ServerException |
Other than exceptions such as RemoteException, stubs use most of the above API; the application itself uses the stub.
Using the JAX-RPC Subset in Applications
The typical steps for creating a JSR 172 JAX-RPC client are:
- Generate a JSR 172 JAX-RPC stub class from a WSDL XML document that describes a remote web service.
- In your code, create an instance of the generated stub.
- After instantiation, invoke methods of the generated stub. These methods correspond to the service endpoint's wsdl:operation in the WSDL XML document.
Generating a JSR 172 JAX-RPC Stub
The client application interacts with a remote service through stubs and the device?s JSR 172 runtime. The runtime hides the complexities associated with management of the connection, SOAP, and data encoding.
To interact with the runtime, the client typically uses stubs generated by a stub generator, or WSDL-to-Java mapping tool, which takes for input a remote service interface definition in a WSDL document, and generates the stub classes that use the JAX-RPC subset API and runtime SPI. Even though you could hand-develop these stubs, using the generator is preferable. The following figure summarizes stub generation:
 |
 |
| |
|
Typical steps for generating a JSR 172 stub.
|
The resulting stub class implements the Stub Interface and the service's remote interface, and uses supporting classes.
|
The steps to generate a JSR 172 JAX-RPC stub are similar to those you follow to create an RMI-based application. In standard RMI, an RMI compiler (rmic) generates a stub from a remote interface definition. As Figure 4 illustrates, when you create a WSA application, a WSDL-to-Java mapping tool generates a stub from a WSDL XML file. The mapping tool generates a remote interface Java file and supporting classes that are used by the generated stub itself. You then compile the JSR 172 stub into a Java class, and package it with the application.
Sun's J2ME Wireless Toolkit, version 2.1, includes a stub generator for JSR 172 JAX-RPC -- I'll tell you how to use it later in this article. Third-party vendors offer their own stub generators.
Using the JSR 172 JAX-RPC Stub
Once the stub is generated, it can be used by your application. The following code snippet uses a service called PubService to retrieve wireless articles:
package j2medeveloper.pubwebservice
// MIDP
import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
...
// JAX-RPC
import java.rmi.RemoteException;
// JAX-RPC
String serviceURL = "www.j2medeveloper.com/pubwebservice";
String articleID = "IntroJSR172";
...
// Instantiate the service stub.
PubService_Stub service = new PubService_Stub();
// Set up the stub.
service._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, serviceURL);
service._setProperty(Stub.SESSION_MAINTAIN_PROPERTY, new Boolean(true));
...
try {
// Invoke the PubService method getArticleByID() to retrieve
// a specific article by ID. A WirelessArticle represents an
// article and has methods to get the article's author, date,
// title, summary, and body. PubService also has a method to
// get the PubService's RSS feed.
WirelessArticle article = service.getArticleByID(articleID);
// Create a Form to display the article.
javax.microedition.lcdui.Form form =
new Form(article.getShortTitle());
form.append(wrap(article.getSummary()));
...
...
display.setCurrent(form);
} catch (RemoteException e) {
// Handle RMI exception.
} catch (Exception e) {
// Handle exception.
}
...
|
Using stubs makes remote service invocation very easy. This code instantiates PubService_Stub, initializes the instance, and invokes one of its methods, getArticleByID() to retrieve the article itself. The application then uses the article's access methods to get the article's title, summary, and so on.
Note that invoking PubService_Stub.getArticleByID() is a blocking call, so in a real application you would dispatch the call in a separate thread. In WSA, method invocation follows the familiar synchronous request-response model of client-server interaction: the client makes a request to the server, and then waits for a response:
 |
| |
|
Figure 5. Synchronous Request-Response Model
|
How the method and its arguments are encoded, serialized, and sent, and how the response is received, decoded, and de-serialized are all transparent to the application. The JSR 172 stub and runtime handle all the tedious details.
Mapping WSDL Data Types to Java
Let's briefly examine WSDL-to-Java data type mapping in JSR 172's JAX-RPC subset API:
 |
xsd:string
|
String |
xsd:int
|
int or Integer |
xsd:long
|
long or Long |
xsd:short
|
short or Short |
xsd:boolean
|
boolean or Boolean |
xsd:byte
|
byte or Byte |
xsd:float
|
String, float, or Float |
xsd:double
|
String, double, or Double |
xsd:QName
|
javax.xml.namespace.QName
|
xsd:base64Binary
|
byte[]
|
xsd:hexBinary
|
byte[]
|
Arrays
|
Based on the schema of the XML array |
xsd:complexType
|
Sequence of primitive and class types |
Remember that CLDC 1.0 does not support floating-point types, so float and double must be mapped to String. Developers should use this default mapping when targets include both CLDC 1.0 and CLDC 1.1 platforms.
For more information about WSDL, please refer to the JSR 172 specification, and W3C's WSDL specification.
Using the JAXP Subset in Applications
Now let's shift our attention to the other JSR 172 API, the JAXP subset. Recall that JAXP and JAX-RPC are both optional packages; they can exist independently of each other.
The JAXP XML-parsing API is based on a strict subset of the Simple API for XML, version 2 (SAX2). Even though most of the classes in the standard SAX2 API are missing in this subset, all the necessary core functionality remains.
The following list describes the JAXP subset at a high level. For more information, please refer to the JSR 172 specification:
- Based on a strict subset of SAX 2.0
 |
 |
 |
 |
 |
SAX 1.0 is not supported, because SAX 2.0 has superseded it.
|
 |
 |
 |
- Supports XML namespaces
- Supports both UTF-8 and UTF-16 character encoding
- Doesn't support either Level 1.0 or Level 2.0 of the Document Object Model (DOM), as it's considered to be too heavy.
- Doesn't support Extensible Stylesheet Language Transformations (XSLT)
- Supports Document Type Definition (DTD)
 |
 |
 |
 |
 |
Parsers can validate XML documents against specified internal or external definitions of their types. JSR 172 specifies that document validation is optional because it's an expensive operation, one that's not suitable to the more restricted CLDC-based environment, but might be successful on higher-end CDC-based platforms. If DTD processing is skipped, the DefaultHandler.skippedEntity() method notifies the application. This logic is in line with the XML 1.0 specification.
|
 |
 |
 |
The following table describes the packages in the JSR 172 JAXP subset:
 |
javax.xml.parsers
|
Defines SAXParser, the API that represents a simple SAX-based parser. |
Defines SAXParserFactory, a factory to obtain and configure a SAX-based parser. |
Defines ParserConfigurationException, thrown to indicate a configuration error. |
Defines FactoryConfigurationError, thrown to indicate a problem with the parser factories. |
org.xml.sax
|
Defines Attributes, an interface that provides access to a list of attributes by:
- Attribute index
- Namespace-qualified name
- Qualified (prefixed) name
|
Defines Locator, an interface for associating a SAX event with a document location. The application can get parsing event location information such as event column number, line number, and public and system identifiers, by using an instance of Locator that is specified by calling the DefaultHandler object's setDocumentLocator() method.
Locator provides methods to get the column and line number where the current document event ends, and to get the public and system identifiers for the current document event.
|
Defines InputSource, which encapsulates an input source. It provides methods to get or set the byte stream, the character set, the encoding, and the public and system identifiers. |
Defines the general SAX exception class, SAXException, and three subclasses: SAXNotRecognizedException, SAXNotSupportedException, SAXParseException |
org.xml.sax.helpers
|
Defines DefaultHandler, a base class for SAX2 event handlers. It provides default implementations of event-handling behavior for applications to extend. To tailor parsing behavior, application developers only need to overwrite the methods of interest.
As the parser encounters XML elements or attributes while reading an XML stream, it notifies the application of certain events by invoking one of the DefaultHandler methods; for example:
startDocument() and endDocument() at the beginning and end of an XML document
startElement() and endElement() at the beginning and end of an XML element
characters() when character data is found inside an element
skippedEntity() when an entity is skipped
The JSR 172 specification describes the rest of the DefaultHandler methods. |
The following method definition shows how to use JAXP to parse an RSS feed retrieved from PubService:
/**
* Invoke the service and parse the RSS feed.
*/
private void parseRSSfeed() {
try {
// Create a RSS parser handler
parser = new RSSParserHandler();
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
saxParser = factory.newSAXParser();
} catch (Exception e) {
// Handle exception...
return;
}
// Invoke the remote service method getRSSFeed to
// retrieve the RSS feed for the Pub service
// web site.
String rssFeed = service.getRSSFeed();
// Parse the RSS feed.
byte[] rssFeedByteArray = rssFeed.getBytes("UTF-8");
ByteArrayInputStream rssFeedByteArrayInputStream =
new ByteArrayInputStream(rssFeedByteArray);
saxParser.parse(rssFeedByteArrayInputStream, parser);
} catch (RemoteException e) {
// Handle RMI exception.
} catch (Exception e) {
// Handle exception.
}
}
|
The parseRSSfeed() method uses the PubService_Stub.getRSSFeed() remote service to retrieve the RSS Content XML document for the PubService, which it then passes to the JAXP RSS-feed XML parser. The following class definition shows how to implement the handler responsible for parsing the RSS feed:
/**
* RSS Parser handler class to parse the RSS feed from the
* server.
*/
class RSSParserHandler extends DefaultHandler {
// Stack to keep track of document parsing.
Stack stack;
// Current document element.
Object current;
/** Start of document processing. */
public void startDocument() {
rssParsingComplete = false;
title = link = description = null;
stack = new Stack();
}
/** Process the new element. */
public void startElement(
String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if ("item".equals(qName)) {
title = link = description = null;
}
stack.push(qName);
}
/** Process the character data for current tag. */
public void characters(
char[] ch, int start, int length) {
Object current = stack.peek();
if ("title".equals(qName)) {
title = new String(ch, start, length));
} else if ("link".equals(qName)) {
link = new String(ch, start, length));
} else if ("description".equals(qName)) {
description = new String(ch, start, length));
}
}
/** Process the end element tag. */
public void endElement(
String uri, String localName, String qName) {
stack.pop();
if ("rss".equals(qName)) {
rssParsingComplete = true;
} else {
// Do something with title, link, description, such
// as displaying it to the user.
}
}
}
|
Note the definitions of startDocument(), startElement(), stopElement(), and characters() methods, which provide the minimum functionality required for SAX2 XML parsing.
J2ME Wireless Toolkit Support for Web Services
The J2ME Wireless Toolkit, version 2.1, includes the libraries you need to develop MIDlets that take full advantage of J2ME Web Services. The toolkit also includes a JAX-RPC stub generator, which you can run from the command line or from the KToolbar menu, as the next figure illustrates:
 |
 |
| |
|
Figure 6. The J2ME Wireless Toolkit 2.1 Utilities and Stub Generator
|
From the Utilities screen choose Stub Generator. In the dialog that appears, enter the URL for the input WSDL document that describes the service to use, the output path for the generated files, and the package name to use for the generated stub files. Select CLDC 1.0 or 1.1 as appropriate.
Version 2.1 of the J2ME Wireless Toolkit also includes a sample application, JSR172Demo.
Summary
The J2ME Web Services Specification standardizes programming interfaces for web services and XML parsing in the J2ME platform. With the advent of the JAX-RPC subset API, developers can use the Java programming language and familiar JAX-RPC APIs to create applications that consume XML-based remote services, without having to deal with the low-level details of HTTP, SOAP, and data marshaling. And with the introduction of the JAXP subset API, XML parsing is now part of the J2ME platform itself. The J2ME Web Services API eliminates the need for developers to include code for remote invocation and XML parsing in each application.
These powerful APIs give mobile applications much easier access to web-based services, but developers must keep in mind the restricted application environment J2ME devices provide. Simply porting existing XML-based desktop or enterprise applications is not the proper way to develop web services clients for the J2ME platform. Due consideration of device processing power, battery life, network bandwidth, and security is essential.
For More Information
Acknowledgements
Many thanks to Jon Ellis and Mark Young, JSR 172 specification leads, for their feedback to this article.
Back To Top
|
|