当前页面: 开发资料首页 → J2ME 专题 → J2ME WEB服务入门教程
摘要: J2ME WEB服务入门教程
作者:C. Enriq 文章来源:SUN中国技术社区
J2ME Web 服务 API( Web Services API,WSA)是作为 JSR 172 在 Java Community Process 中开发的,它拓展了 J2ME, 从而支持 Web 服务。API 的两个可选软件包对两个功能区域进行标准化,这两个功能区域对于 Web 服务的客户机来说是至关重要的:远程服务调用和 XML 解析。 本文将介绍这两个软件包,展示如何使用它们并描述在 J2ME 无线工具包(J2ME Wireless Toolkit)中对它们的支持。
要系统地学习更多关于无线应用程序开发的复杂内容,请参阅文章“The Complexity of Developing Mobile Networked Data Services, J2ME Wireless Connection Wizard For Sun Java System Studio”,该文章描述了移动联网应用程序的主要元素。
简介
WSA 设计用于与 J2ME 配置文件协同工作,J2ME 配置文件要么基于 Connected Device Configuration (CDC),要么基于 Connected Limited Device Configuration (CLDC 1.0 或 CLDC 1.1)。远程调用 API 基于 J2SE 的 Java API for XML-Based RPC (JAX-RPC 1.1) 的一个完整子集,它包含了一些远程方法调用(Remote Method Invocation,RMI)类,以满足 JAX-RPC 依赖。XML 解析 API 基于 Simple API for XML, v2 (SAX2)的一个完整子集。
WSA 的目标是把对 Web 服务调用和 XML 解析的基本支持集成到设备的运行时环境,这样开发人员就不必在每个应用程序中嵌入这样的功能了——在像移动电话和个人数字助理这样资源有限的设备中,这算是一个特别浪费资源的问题。
核心规范
Web Services Interoperability Organization (WS-I) 促进了定义 Web 服务的核心规范和应用层协议,并且它们受 World Wide Web Consortium (W3C) 和 Organization for the Advancement of Structured Information Standards (OASIS) 的指导。4 个关键标准规定了如何创建、部署、发现以及如何使用 Web 服务:
<table class="vatop" cellspacing="0" cellpadding="0" width="100%" summary="Web Services Standard" border="0"><tr class="tablecaption2"><th class="tablecaption2" align="left">Web 服务标准</th><th class="tablecaption2" align="left">描述</th></tr><tr><td>Simple Object Access Protocol (SOAP) 1.1</td><td>定义了传输和数据编码</td></tr><tr class="grey1"><td>Web Services Definition Language (WSDL) 1.1</td><td>定义了如何描述远程服务</td></tr><tr><td>Universal Description, Discovery, & Integration (UDDI) 2.0</td><td>定义了如何发现远程服务</td></tr><tr class="grey1"><td>Extensible Markup Language (XML) 1.0 和 XML Schema</td><td>定义了可扩展标记语言(Extensible Markup Language ,XML) 和 XML 模式(Schema)</td></tr></table>这些主要规范往往非常广泛,而且 Web 服务开发人员已发现难以实现完全互操作性。为了解决标准解释中存在的差异,WS-I 已定义了一组称作 WS-I Basic Profile, version 1.0 的一致性规则。JSR 172 符合基本配置文件(Basic Profile)。
J2ME 平台上的 Web 服务
JSR 172 规定了标准化客户端技术,允许 J2ME 应用程序在典型 Web 服务架构上使用远程服务,如图 1 所示:<table cellspacing="0" cellpadding="0" width="100" align="center" border="0">
<tr><td></td></tr><tr><td class="grey3" align="center" height="24">图 1 在典型 Web 服务架构上的 J2ME Web 服务 </td></tr></table>在高层,该 Web 服务架构具有三个元素:
WSA 的第一个版本只解决了 Web 服务的使用,不支持服务端点的创建和开发;J2ME 设备可以是服务用户,但不可以是服务提供者。JSR 172 还没有规定使用 UDDI 的服务发现的 API。
了解 JSR 172
首先考查如何组织典型的基于 JSR 172 的应用程序,开始了解 J2ME Web Services 是如何运作的:<table cellspacing="0" cellpadding="0" width="100" align="center" border="0"><tr><td></td></tr></table>该应用程序本身是一个基于移动信息设备配置文件(Mobile Information Device Profile,MIDP)或个人基础配置文件(Personal Basis Profile,PBP))的智能客户机,具有特定于业务的逻辑、用户界面、持久性逻辑以及生命周期和应用程序状态管理。该应用程序可以使用 JAXP 子集 API 来处理 XML 文档。还可以使用 JAX-RPC 子集 API 来使用 Web 服务,从而使用 JSR 172 存根和运行库。
在像手提电话这样的设备中,应用程序和 JSR 172 存根通常驻留在设备内存中,而所有 JSR 172 元素连同基础配置文件和配置一起嵌入到设备本身。
JSR 172 运行库和服务提供者接口
在 JSR 172 操作的中心是运行库,带有服务提供者接口,允许存根执行所有与调用 RPC 服务端点有关的任务:
图 3 描绘出了客户机应用程序、存根以及运行库三者之间的关系:
<table cellspacing="0" cellpadding="0" width="100" align="center" border="0">
<tr><td></td></tr><tr><td class="grey3" align="center" height="24">图 3 JSR 172 运行库和 SPI </td></tr></table>虽然 JSR 172 运行库隐藏了像连接管理和数据编码这样的复杂性,但 SPI 从运行库实现细节分离出了存根,从而允许存根在供应商实现之间的可移植性。
客户机应用程序不直接与运行库和 SPI 进行交互;而是使用存根。运行库和 SPI 主要为打算开发 JSR 172 运行库和自动化工具的第三方供应商所关心,比如开发人员用于生成存根的 WSDL 到 Java 映射工具。
由于应用程序开发人员不直接使用 SPI,因此本文不会较详尽地介绍运行库和 SPI。如果想了解关于它们的更多信息,可以查阅 JSR 172 规范。
JSR 172 JAX-RPC 子集 API
正如前面所提到的,JSR 172 Web 服务远程调用 API 基于 J2SE 的 JAX-RPC 1.1 API 的一个完整子集。以下列表在高层描述了该子集。要了解详细信息,请参阅 JSR 172 specification:
使用存根会使远程服务调用变得非常容易。该代码用例子说明了 PubService_Stub,初始化了实例,并调用了它的其中一个方法 getArticleByID(),以便对文章本身进行检索。然后,应用程序使用了文章的访问方法来获取文章的标题、摘要等等。
注意,因为调用 PubService_Stub.getArticleByID() 是分块调用,因此在实际应用程序中,要用分离的线程来分派该调用。在 WSA 中,方法调用遵循常见的客户机/服务器交互的同步请求-响应模型:客户机向服务器发出请求,然后等待服务器的响应:
<table cellspacing="0" cellpadding="0" width="100" align="center" border="0">
<tr><td></td></tr><tr></tr><tr><td class="grey3" align="center" height="24">图 5 同步请求/响应模型</td></tr></table>如何对方法及其参数进行编码、序列化和发送,以及如何接收、解码和反序列化响应,对应用程序来说是完全透明的。JSR 172 存根和运行库处理所有这些单调冗长的细节。
将 WSDL 数据类型映射到 Java
下面简要考察一下 JSR 172 的 JAX-RPC 子集 API 中的 WSDL 到 Java 的数据类型映射:
* 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. } }parseRSSfeed()
方法使用 PubService_Stub.getRSSFeed()
远程服务来检索 PubService 的 RSS Content XML 文档,然后将其传递到 JAXP RSS-feed XML 解析器。以下类定义显示了如何实现负责解析 RSS 提要的处理程序:
/** * 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. } } }
注意,startDocument()
、startElement()
、stopElement()
以及 characters()
方法的定义提供了 SAX2 XML 解析所需的最低功能。
支持 Web 服务的 J2ME 无线工具包
J2ME Wireless Toolkit v2.1 包含开发充分利用 J2ME Web Service 的 MIDlet 所需的库。该工具包还包含 JAX-RPC 存根生成器,可以从命令行或 KToolbar 菜单运行它,如下图所示:
<table cellspacing="10" cellpadding="0" align="center" border="0">
<tr valign="top"><td></td><td></td></tr><tr><td class="grey3" align="center" colspan="2" height="24">图 6 J2ME 无线工具包 2.1 实用程序和存根生成器</td></tr></table>在 Utilities 屏幕上选择 Stub Generator。在出现的对话框中,输入描述要使用服务的输入 WSDL 文档的 URL、生成文件的输出路径以及用于已生成存根文件的软件包名。相应地选择 CLDC 1.0 或 1.1。
J2ME 无线工具包的 v2.1 还包含一个示例应用程序 JSR172Demo。
结束语
J2ME Web 服务规范(J2ME Web Services Specification)使 J2ME 平台上的 Web 服务和 XML 解析编程接口标准化了。随着 JAX-RPC 子集 API 的出现,开发人员可以使用 Java 编程语言和熟悉的 JAX-RPC API 来创建使用基于 XML 的远程服务的应用程序。而不必处理 HTTP、SOAP 和数据转换的底层细节。而且随着 JAXP 子集 API 的引入,XML 解析目前已是 J2ME 平台本身不可分割的一部分。J2ME Web Services API 消除了开发人员在每个应用程序中包含用于远程调用和 XML 解析代码的需要。
这些功能强大的 API 允许移动应用程序更容易地访问基于 Web 的服务,但是开发人员千万别忘了 J2ME 设备提供的受限制的应用程序环境。仅仅移植现有的基于 XML 的桌面或企业应用程序不是开发 J2ME 平台的 Web 服务客户机的适当方法。对设备处理能力、电池寿命、网络带宽以及安全性的适当考虑也是不可缺少的。
更多信息
致谢
非常感谢 JSR 172 规范负责人 Jon Ellis 和 Mark Young 对本文的反馈。