今天的 Web 开发人员和架构师能够开发出具有更好用户体验的动态 Web 应用程序。改变 Web 应用程序框架的范型带来了一些挑战。了解如何在创建 mashup 示例应用程序的过程中利用 XQuery 有效地应对这些挑战。
Web 2.0 应用程序为用户提供了更动态的体验,常常使用 Asynchronous JavaScript and XML (Ajax)。加载了 HTML 主文档之后,可以使用 Ajax 更新 Web 内容而不需要刷新整个网页。使用 Ajax,可以把 Web 内容看作是能够随时推出的数据片段。此外,客户端 JavaScript 过程能够自己格式化数据,无论普通文本、XML、JavaScript Object Notation (JSON) 对象还是 HTML 片段形式的数据。
新的 Web 应用程序开发范型远远超出了传统的 “模型-视图-控制器(MVC)” 模型。不幸的是,很多 Ajax Web 应用程序框架,如 Direct Web Remoting (DWR) 和 JavaServer Faces (JSF) 都带来了一些挑战。尽管 Ajax 框架可以在很多方面简化应用程序开发,但框架的机制或多或少地控制着 Web 内容。很多现有的 Ajax 没有提供能够解决现实世界复杂性的统一解决方案,尤其是来自 XML 的数据集成和来自 RSS 提要的内容联合的要求越来越高。这些新的需求表明在服务器端需要高效的 Web 服务支持。
为了利用这些变化的技术,可以创建 mashup 应用程序将 Web 内容与 XML 数据和 Web 服务结合起来。我将说明如何使用 XQuery(一种用于 Web 服务的很有前途的技术)和 XML 创建一个 mashup 应用程序。
这个应用程序允许用户在当地所有图书馆组成的公共图书馆系统中搜索新书。每个图书馆中,管理员都定期购买新书,但不知道其他公共图书馆是否已经购买了这些书。这个应用程序通过 Web 服务发布和共享它存储的信息。应用程序从图书馆中汇集了所有的新书资料,为用户提供了搜索新书的功能。
XQuery 来救援
XQuery 是一种万维网联盟(W3C)标准,专门为从 XML 文档中提取信息而设计。它的 XML 处理能力和已有的面向对象编程模型如 Java™ 文档对象模型(DOM)API 相比有很多优势(有关的 IBM developerWorks 资源请参阅 参考资料 部分)。
简化服务器端 XML 处理
可以通过 JavaScript 使用 DOM API 来解析 XML。但是,这并非意味着可以用 JavaScript 在客户端完成所有的 XML 处理。尤其是现在业务逻辑处理变得越来越复杂。如何平衡客户端和服务器端的 XML 处理是设计 Web 应用程序时要考虑的基本问题之一。XQuery 将复杂的业务逻辑留给了服务器端,这样做有很多好处。比方说,可以更全面地考虑安全问题,可以更容易地维护应用程序。此外,XQuery 函数还简化了整个开发过程。
限制在网络上传输的内容
设计 Web 应用程序时,必须考虑有多少内容在网络上传输以及传输的频率高还是低。对服务器的每次请求都会产生开销。应避免对每次按键或鼠标移动都发出异步调用。如果不能正确地使用服务器调用,很容易就会让不必要的网络流量和负荷使应用程序陷于困境。
56K 的拨号网络传输 100KB 的 Web 内容需要 14 秒钟。XQuery 通过减少在网络上传输的内容提供了更好的用户体验。比如,为了限制 XML 内容的大小,可以在返回 XML 之前调用标准 XQuery 函数 string-length()。不过这个例子没有限制 XML 内容的大小,而是通过计算节点数把 XML 元素限制为 100 个 book 项。
集成多个搜索字段
有时候,Web 搜索使用多个项作为搜索条件。比如,这个示例应用程序为用户提供了三个选择。用户可以输入其中的一个字段或者多个字段组合(最多三个)。XQuery 函数与 XPath 表达式的结合为实现搜索需求提供了强大的搜索机制。
不需要另外编程集成业务逻辑
XQuery 是一种声明性语言。为了使用 XML 简化已有数据和服务的集成,它紧凑地封装了业务逻辑。查询输出可以采用用户定义的格式,这样不需要另外编程就能很容易地实现业务逻辑。


|
回页首 |
|
示例应用程序
图 1 显示了应用程序设计。服务器响应可以是客户要求的任何格式,包括 HTML 片段、XML、普通文本或者 JSON 对象。内嵌的 XQuery 根据动态搜索条件执行预定义的查询。在查询中定义业务逻辑和表示逻辑。XML 数据可以来自多个数据源,如文件系统、数据库或者表示 Representational State Transfer (REST) Web 服务的 URI。
图 1. 示例应用程序体系结构
嵌入 XQuery 引擎
可以使用简单的 Java servlet 嵌入 XQuery 引擎。有很多 XQuery 引擎可供选择,既有厂商提供的也有开放源代码的。该应用程序使用开放源代码的 Saxon 8.7(如 清单 1 所示)。
清单 1. 嵌入 Saxon XQuery 引擎
Configuration config = new Configuration();
staticQueryContext = new StaticQueryContext(config);
dynamicQueryContext = new DynamicQueryContext(config);
|
收集 XML 数据和定义数据格式
为了收集 XML 数据,需要考虑 XML 数据来自何处、是否需要使用文档类型定义(DTD)或模式、是否需要执行 XML 验证。该程序对 XML 数据格式使用了一个 DTD,并假定在 XQuery 处理之前所有的数据源都是对该 DTD 有效的 XML 文档。清单 2 给出了 XML 数据格式的一个例子。
清单 2. 示例 XML 数据格式
<?xml version="1.0" encoding="UTF-8"? >
<!DOCTYPE library SYSTEM "dtd/book.dtd">
<booklist>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<authors>
<author>Giada De Laurentiis</author>
</authors>
<isbn></isbn>
<numberCopies>1</numberCopies>
<status>On Shelf</status>
<year>2005</year>
<library>John C. Hart Library</library>
<price>30.00</price>
</book>
...
</booklist>
|
用业务逻辑定义 XQuery 查询
在 XQuery 业务逻辑中,使用三个字段搜索图书,需要建立六种查询策略。此外,还需要考虑没有提供搜索参数的默认场景。与传统的 Web 应用程序框架不同,Xquery 很容易在查询文件中构造业务逻辑。如果需要可以在以后修改该文件,甚至不需要停止应用程序的运行。比方说,可以将查询改为查找最近三年中出版的新书。
XML 数据可能有不同的来源,book_collection.xml 文件定义了一个 XML 数据集合。一个 XML 数据来源是这个应用程序本身:http://localhost:9080/bookinfor/newbooks_localhost.xml。测试该应用程序时,一定要将 Web 应用程序上下文根目录设置正确,如 bookinfor。清单 3 给出了一个 XML 数据集合。
清单 3. XQuery 集合
<collection >
<doc href="newbooks_library_01.xml"/>
<doc href="newbooks_library_02.xml"/>
<doc href="newbooks_library_03.xml"/>
<doc href="newbooks_library_04.xml"/>
<doc href="http://localhost:9080/bookinfor/newbooks_localhost.xml"/>
</collection>
|
设计 XQuery Web 应用程序时务必要考虑性能问题。在这里,我把搜索输出的阈值设为 100 本书。与传统的对象编程如 Java 相比,XQuery 实现业务逻辑不需要服务器端的开发。在处理大量 XML 数据时,可以把内嵌 XQuery 引擎与 IBM DB2 Viper 版结合起来,它能够处理大量 XML 内容,最高可达 2GB。清单 4 显示了 booklist_app.xql 文件中的应用程序业务逻辑。
清单 4. 包含简单业务逻辑的 XQuery
(: -------------------------- :)
(: declares ......:)
(: -------------------------- :)
declare namespace local = "http://localhost/ns";
declare variable $title as xs:string external;
declare variable $author as xs:string external;
declare variable $year as xs:string external;
declare variable $emptymsg as xs:string external;
declare variable $oversizedmsg as xs:string external;
declare variable $outputformat as xs:string external;
(: -------------------------- :)
(: functions :)
(: -------------------------- :)
declare function local:error_view($helpmsg as xs:string) {
if($helpmsg) then $helpmsg
else ('<report status="0">Please call 1-800-123-4567</report>')
};
declare function local:html_view($doc) {
<table>
<tr> <td>Library Name</td>
<td>Book Title</td>
<td>Number of Copies</td>
<td>Status</td>
</tr>
{ for $book in $doc/book
where $book/title/@lang="en"
order by $book/library
return <tr>
<td>{$book/library}</td>
<td>{$book/title/text()}</td>
<td>{$book/numberCopies}</td>
<td>{$book/status}</td>
</tr>
}
</table>
};
(: -------------------------- :)
(: the business logic :)
(: -------------------------- :)
let $docs := ('bookinfor/book_collection.xml')
let $searchdoc := (
<report status='1'>
{
if($title and $year and $author) then (
for $doc in collection($docs)//booklist/book[contains(title,$title)
and (year = $year)]
where contains($doc//authors, $author)
return $doc
) else if($title and $year ) then (
for $doc in collection($docs)//booklist/book[contains(title,$title)
and (year = $year)]
order by $doc//booklist/book/title
return ($doc)
) else if($title and $author ) then (
for $doc in collection($docs)//booklist/book[contains(title,$title)]
where contains($doc//authors, $author)
order by $doc//booklist/book/title
return ($doc)
) else if($author and $year ) then (
for $doc in collection($docs)//booklist/book[(year=$year)]
where contains($doc//authors, $author)
order by $doc//booklist/book/title
return ($doc)
) else if($title) then (
for $doc in collection($docs)//booklist/book[contains(title,$title)]
order by $doc//booklist/book/title
return ($doc)
) else if($author) then (
for $doc in collection($docs)//booklist/book
where contains($doc//authors, $author)
order by $doc//booklist/book/title
return ($doc)
) else if($year) then (
for $doc in collection($docs)//booklist/book[(year=$year)]
order by $doc//booklist/book/title
return ($doc)
) else (
for $doc in collection($docs)
return ($doc//booklist/book)
)
}
</report>
)
(: -------------------------- :)
(: return results :)
(: -------------------------- :)
return if(count($searchdoc//book) eq 0) then local:error_view($emptymsg )
else if(count($searchdoc//book) gt 100)
then local:error_view($oversizedmsg)
else if($outputformat eq $html)
then local:html_view($searchdoc)
else $searchdoc
|
在应用程序中调用 XQuery 查询
为了在内嵌引擎中执行查询,需要设置相关的参数并预编译查询。查询执行后,最终结果发送回 Web 客户机。客户端 JavaScript 代码组装数据并呈现给用户。请注意,这个示例应用程序将目录 c:/tmp/xmldatasources/ 设置为静态的查询上下文。一定要保证 bookinfor 目录在其中,并在测试该应用程序之前将 XML 数据文件复制过去。清单 5 显示了内嵌的 XQuery 调用。
清单 5. 在应用程序中调用 XQuery 查询
dynamicQueryContext.setParameter("emptymsg", "No results exist!");
dynamicQueryContext.setParameter("oversizedmsg",
"Too many results, please refine queries");
staticQueryContext.setBaseURI("c:/tmp/xmldatasources/");
fileReader = new FileReader(request.getRealPath("/")+ "/queries/booklist_app.xql");
queryExpression = staticQueryContext.compileQuery(fileReader);
writer = new StringWriter();
StreamResult result = new StreamResult(writer);
Properties props = new Properties();
queryExpression.run(dynamicQueryContext, result, props);
retContent = writer.getBuffer();
|


|
回页首 |
|
结束语
需要注意的是,XQuery 技术仍然存在一些局限性,学起来也不是很简单。要记住,没有一种技术能够解决所有的业务问题。很可能仍然需要把 XQuery 与其他技术(如 XPath 和 XSLT)结合起来才能满足要求。
虽然 XQuery 还没有成为最终标准,但它的主要特性和功能已经引起了很大关注。它有强大的 XML 处理功能,而且在建立 marshup 应用程序方面显示了很大的潜力。一旦用 XQuery 实现了您的动态 Web 应用程序,可以很快地扩展您的网站,并以相对简单和敏捷的方式达到您的业务目标。


|
回页首 |
|
下载
| 描述 | 名字 | 大小 | 下载方法 |
| Sample code for XQuery to power mashups |
x-xquerymashup.zip |
12KB |
HTTP |
|