| Beehive 是带来众多 Workshop 魔法的巫师,在 Apache Software Foundation ,它已经到达了成熟阶段。然而,在我们刚刚听说 Beehive 之前, BEA 向 Apache 提交了另一个项目: XMLBean 。到 2004 年 6 月为止, XMLBean 已经作为一个羽翼已丰的 Apache Software Foundation Project 得到人们的接受。
XMLBean 是一种 XML-Java 绑定工具。是的,它还是另一种 XML-Java 绑定工具,但是它的出现并未伴随一些新的举措。 XML 已经证实是一种用于结构化数据的、功能强大的有用格式。当前的 XML-Java 绑定工具有它们各自的优点和缺点,但是每种工具使用起来都被认为都有些单调。 XMLBean 通过把 XML 实例和底层架构映射为 JavaBean 风格的对象,克服了单调的缺点。现在,可以通过 getter 和 setter 访问器方法定位、检索和操作 XML 实例数据,这两种方法是任何开发人员都可以轻松使用的通用方法。
XMLBean 是通过编译 XML Schema 而创建的。 XML Schema 是一种用来为 XML 文档提供准则的语言。包括特定 XML 文档必须遵守的规则的模式才能被认作是合法模式。 XML Schema 可以变得相当复杂,这取决于它们描述的 XML 实例。这种复杂性对于 XMLBean 来说无足轻重:无论有多复杂,您的模式可以被编译为 XMLBean 。
我们将讨论一个用于虚构的汽车代理的库存管理系统,这个应用程序完全使用 XMLBean 编写,它不仅演示了如何在 Web service 中使用 XMLBean ,而且还演示了从数据库到用户界面的整个过程中,它们被用作数据对象的情形。我们将重点强调这个应用程序的重要方面,但是您可以下载 整个应用程序 的源代码。这个示例应用程序依赖于一个数据库表,这个表中将保存我们的库存。表中的大多数列都很直观,除了最后一列。 STATUS 列保存的值是二取一的,或者是 new ,或者是 used ,稍后会用该值来过滤我们的结果(参见表 1 )。
|
VIN
|
MAKE
|
MODEL
|
YEAR
|
PRICE
|
STATUS
|
表 1 车辆数据
库存表用于保存车辆信息
创建一个模式
XMLBean 来源于 XML Schema 的编译。库存管理 Web service 将返回来自表的车辆列表,而我们需要创建一个 XML Schema 来反映这个列表。就像 JDBC 把 SQL 类型映射为 Java 类型那样, XMLBean 把模式类型映射为 Java 类型。通过考虑表的结构,您可以想像得到,我们只需要和两种类型的数据打交道。在 Java 世界中,这两种类型是 String 和 int (价格是 int 类型的,因为大多数汽车的价格都没有列出分币,但是我们也可以使用 float 类型)。在模式世界中,这些类型也是 string 和 int 。尽管在我们的例子中,我们使用了简单类型,但是 XMLBean 可以处理任何模式类型。清单 1 显示了完整的 XML Schema 。注意,这里定义了两个顶层元素—— vehicleList ,它只是第二个元素 vehicle 的列表; vehicle 元素包含我们的库存表中每一列的节点定义。
XML Schema
清单 1. 在完整的 XML Schema 中定义了两个顶层元素。 vehicleList 元素是第二个元素的列表, vehicle 元素包含我们的库存表中每一列的节点定义。
<?xml version="1.0"?>
<xs:schema xmlns="http://example/schema"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example/schema">
<xs:element name="vehicleList">
<xs:complexType>
<xs:sequence>
<xs:element ref="vehicle" minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="vehicle">
<xs:complexType>
<xs:sequence>
<xs:element name="vin" type="xs:string"/>
<xs:element name="status" type="xs:string"/>
<xs:element name="make" type="xs:string"/>
<xs:element name="model" type="xs:string"/>
<xs:element name="year" type="xs:int"/>
<xs:element name="price" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
既然已经创建了 XML Schema ,必须把它编译为 XMLBean 。如果您使用的是 XMLBean 项目(您可以从 Apache 站点上下载),那么可以通过 Schema Compiler 实用程序或者特殊的 xmlbean Ant 任务来执行这种编译。也可以使用 scomp 实用程序以各种方式编译模式,您可以在 XMLBean 发布的 bin 目录中找到这个实用程序。(请参阅这个实用程序的使用文档,以获得更多相关信息。) xbean.jar 文件位于 XMLBean 发布的 lib 目录中,它是 xmlbean Ant 任务的容器。通过 Ant 任务进行的模式编译具有明显的优点,即允许我们将模式编译放在其他常见的构建任务中。
如果使用的是 WebLogic Workshop ,那么可以简单地在应用程序中创建一个模式项目,并导入模式。使用模式项目时,每次在项目中导入或修改模式时,编译都会自动进行。图 1 显示了把我们的模式导入一个模式项目后的结果。为每个顶级元素都创建了一个以后缀 Document 结尾的对象。这些对象代表我们的模式中定义整个元素或类型。它们是为访问、操作和创建符合给定类型的 XML 提供入口点的工厂。 XML 类型是由嵌入的工厂类 : VehicleListDocument.VehicleList 和 VehicleDocument.Vehicle 表示的。这些类访问了 XML 实例数据本身。
图 1. 模式导入
模式项目自动编译模式
我们的库存管理服务将提供一些操作。这些功能之一就是从我们的库存表返回所有的车辆。其他两项功能允许客户机更新或者创建库存中的车辆。每项任务单独使用 XMLBean 进行通信。
任务管理
我们需要创建一个数据库控件来管理这些任务。这个控件必须提供一个方法,从数据库选择车辆信息,返回车辆的一个列表,并提供接受车辆信息来更新或插入数据库的方法。通常,我们会创建一个值对象,并将这个对象用作控件的方法的返回类型和参数类型。然而,因为已经在库存表之后对 XMLBean 进行了建模,而且知道将在服务中使用这些对象,所以我们可以简单地把信息直接检索到 XMLBean 中,从而简化整个服务。在这个代码片断中,数据库控件的部分显示了一个 getAllVehicles() 方法,用于简单地返回一个 VehicleDocument.Vehicles 的数组,以及 insertVehicle() 方法,用于接受一个 VehicleDocument.Vehicle 对象作为参数。
/**
* :sql statement="SELECT *
* FROM INVENTORY"
*/
VehicleDocument.Vehicle[]
getAllVehicles();
/**
* :sql statement="INSERT INTO
* INVENTORY VALUES
* ({vehicle.vin},
* {vehicle.make},
* {vehicle.model},
* {vehicle.year},
* {vehicle.price},
* {vehicle.status})"
*/
void insertVehicle(
VehicleDocument.
Vehicle vehicle);
因为 XMLBean 拥有 getter 和 setter 访问器方法,所以可以在 SQL 表达式中直接使用它,就好像它是一个值对象一样。 Web service 将使用这个数据库控件来检索和维护库存信息。当控件使用代表 XML 实例文档的嵌入类时, Web service 不能以这种方式转换内部类,它们必须单独与外部的工厂类打交道。这种情况是一个小问题。插入和更新方法将接收 VehicleDocument 作为一个参数,以便获得内部的 VehicleDocument.Vehicle 类,我们将使用访问器方法 getVehicle() 。 Web service 的 insertVehicle() 方法看起来如下所示:
public void insertVehicle(
VehicleDocument vehicleDoc)
{
VehicleDocument.Vehicle
vehicle = vehicleDoc.
getVehicle();
vehicleControl.
insertVehicle(vehicle);
}
更新库存中某一项的方法通常都一样, SQL 语句是个例外。从工厂获取 XML 实例表示是一个一步就可以完成的操作。获取库存要稍微复杂一点,但是也不会复杂很多。我们的控件返回一个 VehicleDocument.Vehicles 的数组。另一方面, Web service 将返回一个 VehicleListDocument 。幸运的是, VehicleListDocument 只是一个代表 VehicleDocument.Vehicle 对象数组的工厂类。我们需要做的就是创建一个新的 VehicleListDocument ,用它来创建新的 VehicleListDocument.VehicleList ,然后把它赋给我们获得的 VehicleDocument.Vehicle 数组。
public VehicleListDocument
findAllVehicles()
{
VehicleDocument.Vehicle[]
vehicles = vehicleControl.
getAllVehicles();
VehicleListDocument
vehicleListDoc =
VehicleListDocument.
Factory.newInstance();
VehicleListDocument.
VehicleList vehicleList =
vehicleListDoc.
addNewVehicleList();
vehicleList.setVehicleArray(
vehicles);
return vehicleListDoc;
}
XMLBean 对象可以用于快速而轻松地创建新的 XML 实例。
在创建了 Web service 之后,可以生成 Web service 描述语言( Web Services Description Language , WSDL )文件,然后使用它创建一个 Web service 客户机。生成客户机时, Workshop 会自动识别我们的 XMLBean 和服务中的方法是匹配的(参见 图 2 )。这种通知没有什么大的用处,因为我们开发了 Web service 来指定返回 XMLBean ,而且在这里,我必须同意。然而,我们很快就会讨论与此相同的通知,但是涉及的环境要更广。
图 2. 通知
Workshop 识别类已经访问了基于 WSDL 文件的 Web service
Web service 客户机
我们的 PageFlow 将依赖于服务客户机管理车辆库存。 PageFlow 应该为用户提供查看库存中的所有项目和修改并创建项目的能力。此外,为了进一步演示 XMLBean 的强大功能,我们还允许用户基于车辆的状态( new 或者 used )过滤结果。这个过滤器在幕后使用 XPath 来选择适当的车辆。 XPath 是用来基于某种标准在 XML 文档内定位特定元素的一种语法。我们将简要讨论 PageFlow 中涉及到的功能的主要部分。(可以在线查看完整的 源代码 。)
首先,让我们考察如何将库存显示给用户。我们知道,我们的库存服务会返回一个 VehicleListDocument ,而且还知道,这个对象基本上是 VehicleDocument.Vehicle 数组的表示。该数组将会是用于我们的显示的完美对象。在从 VehicleListDocument 获取 VehicleDocument.Vehicle 数组的几个步骤中,库存变量是在 PageFlow 作用域内声明的,这样就可以从 JavaServer Pages ( JSP )中访问它。
vehicleListDoc = vehicleInventory.
findAllVehicles();
inventory = vehicleListDoc.
getVehicleList().
getVehicleArray();
VehicleDocument.Vehicle[] 数组允许我们轻松地显示整个库存。现在,我们有一个准备好显示的对象数组。可以肯定的是,我们都习惯于在这些实例中使用 JavaBeans 和 NetUI 标签来显示数据列表。在 NetUI 标签中,会指定将显示的属性名,并通过反射从内部调用适当的访问器方法。因为值对象类似于 XMLBean 的属性,所以可以使用 VehicleDocument.Vehicles ,就像使用 JavaBean 那样显示它们。
在 JSP 上,只需要把 NetUI repeater 标签从调色板拖到编辑窗格中,就可以显示 Repeater 向导。在 Data Source 框中,先指定 pageFlow.inventory ,然后单击 Create (参见图 3 )。这个步骤是显示库存的过程中惟一必不可少的一步(参见清单 2 中生成的 repeater 代码块)。注意,容器的每一项都是一个 VehicleDocument.Vehicle 。尽管这个对象是 XML 实例文档的代表,但是可以通过名称轻松访问车辆的子元素。
图 3. 库存显示
把一个 NetUI repeater 标签从调色板拖拉到编辑窗格中,就可以显示 Repeater 向导。为一步式方法指定一个数据源,以显示库存。
Repeater 标签
清单 2. 使用数据 repeater 标签,这是显示库存过程中惟一必不可少的一步。
<netui-data:repeater dataSource=
"{pageFlow.inventory}">
<netui-data:repeaterHeader>
<table class="tablebody" border="1">
<tr class="tablehead" valign="top">
<th>Make</th>
<th>Model</th>
<th>Price</th>
<th>Status</th>
<th>Vin</th>
<th>Year</th>
</tr>
</netui-data:repeaterHeader>
<netui-data:repeaterItem>
<tr valign="top">
<td><netui:label value="{container.item.make}"
defaultValue=" "></netui:label></td>
<td><netui:label value="{container.item.model}"
defaultValue=" "></netui:label></td>
<td><netui:label value="{container.item.price}"
defaultValue=" "></netui:label></td>
<td><netui:label value="{container.item.status}"
defaultValue=" "></netui:label></td>
<td><netui:label value="{container.item.vin}"
defaultValue=" "></netui:label></td>
<td><netui:label value="{container.item.year}"
defaultValue=" "></netui:label></td>
</tr>
</netui-data:repeaterItem>
<netui-data:repeaterFooter></table>
</netui-data:repeaterFooter>
</netui-data:repeater>
对于库存管理系统的更新和插入部分,创建一个 form bean 和数据项页面,就像您通常所做的那样。随着一辆已修改或新的车辆的提交,需要使用已提交的 form bean 来创建 VehicleDocument ——库存管理服务的预期参数。
在该例中,我们不仅使用服务把新的车辆插入到数据库中,而且更新了用于显示数据的库存数组,这样就可以在不必从服务检索新的 VehicleListDocument 的情况下显示新的车辆。在实际的解决方案中,可以在每次更新之后重新进行检索,以确保库存列表保持最新。
更新库存
第一步是使用以前获得的 VehicleListDocument 来获得 VehicleListDocument.VehicleList ,并为它添加一个新的 VehicleDocument.Vehicle 。
VehicleDocument.Vehicle vehicle =
vehicleListDoc.getVehicleList().
addNewVehicle();
接下来,将联合使用 form bean 访问器方法和 XMLBean 访问器方法来设置 VehicleDocument.Vehicle 的属性:
private static final String
NEW_QUERY =
"declare namespace xq=
'http://example/schema'"
接下来需要构造查询部分。我们想构造一个可以检索状态为 new 的所有车辆元素的查询。 XPath 中的等价做法是:
vehicle.setMake(form.getMake());
vehicle.setModel(form.getModel());
vehicle.setYear(form.getYear());
vehicle.setPrice(form.getPrice());
vehicle.setVin(form.getVin());
vehicle.setStatus(
form.getStatus());
既然 XMLBean 包含来自表单的值,那么可以创建一个新的 VehicleDocument 实例,设置新创建的 VehicleDocument.Vehicle ,并调用服务的合适方法,把它传递给 VehicleDocument :
VehicleDocument vehicleDoc =
VehicleDocument.Factory.
newInstance();
vehicleDoc.setVehicle(vehicle);
vehicleInventory.insertVehicle(
vehicleDoc);
最后,更新用于显示的库存数组,因为我们刚刚给它添加了一辆新车:
inventory = vehicleListDoc.
getVehicleList().
getVehicleArray();
更新库存中的项目和插入它们的操作几乎相同。惟一的差别在于从库存数组获得了 VehicleDocument.Vehicle 对象,而不是给它添加一个这样的对象,因为我们准备修改一辆现有的车辆。 XPath 可以用于过滤显示在我们的库存管理系统中的结果。在我们的库存管理系统中,要探讨的最后一部分功能是基于车辆的状态过滤结果,状态的值是 new 或 used 。
第一步是构造一个 XPath 查询。我们将集中构造一个新车辆的查询,这由两个部分组成:一个名称空间声明和一个基于路径的查询。在该例中,参考原来的模式,可以看到我们的名称空间是 http://example/schema 。下面是查询的开始部分:
private static final String
NEW_QUERY =
"declare namespace xq=
'http://example/schema'"
接下来需要构造查询部分。我们想构造一个可以检索状态为 new 的所有车辆元素的查询。 XPath 中的等价做法是:
this/xq:vehicleList/xq:
vehicle[status='New']
我们把这个查询添加到表单的名称空间声明中:
private static final String
NEW_QUERY =
"declare namespace xq=
'http://example/schema'" +
"/xq:vehicleList/xq:
vehicle[status='New']";
在我们的操作方法中,将使用这个查询来过滤库存,您可以使用下面的代码来完成过滤操作:
XmlObject[] vehicles = (
XmlObject[])vehicleListDoc.
selectPath(NEW_QUERY);
if (vehicles.length > 0) {
inventory = (
VehicleDocument.
Vehicle[])vehicles;
}
从以前服务接收的 VehicleListDocument 开始,使用查询来选择由该查询定义的路径。 selectPath() 方法返回一个 XMLObject 的数组, XMLObject 是所有 XMLBean 的基类。因为我们知道已经查询过了 VehicleDocument.Vehicles ,所以可以安全地将它转换为特定的 XMLBean 类型,并更新库存数组。重新显示的时候,就可以只查看满足查询条件的车辆了。
利用选择
到现在为止,我们已经了解到, XMLBean 与 Web service 、 PageFlow 甚至数据控件的集成要相对容易一些。在该例中,我们有机会开发自己的服务,规定它返回我们选定的 XMLBean 。然而,很多时候这是不可行的,我们会发现自己把外部服务集成到了应用程序中。 Web service 出版商的数量不断增长——包括像 Yahoo! 、 Google 、 eBay 和 Amazon 这样的公司,这些公司允许我们与它们的著名服务进行集成。我们的任务是在应用程序中利用这些服务,只访问描述该服务的相关 WSDL 。
尽管听起来好像是不同的情形,实际上,它们是完全相同的。我们可以把这个 WSDL 文件编译为 XMLBean ,就像我们对 VehicleList.xsd 文件所做的那样。一旦我们的 XMLBean 可用,就可以从 WSDL 生成 Web service 客户机,而 Workshop 将再次意识到,我们拥有与 Web service 的方法相关的类型,并采用我们的 XMLBean 。在这种情况下, XMLBean 带给我们的优势就在其他技术之上,特别是因为我们需要的所有数据对象都已经通过一些鼠标单击为我们创建好了。希望这里给出的例子能够使您的视线触及到 XMLBean 可以完成的一些事情上,而且我们鼓励您在自己的应用程序中亲身体验使用它们的好处。 |