Web 服务建模,第 1 部分

XML 模式

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: Web 服务建模,第 1 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:Web 服务建模,第 1 部分

敬请期待该系列的后续内容。

序言

这一系列的文章详细描述了如何建立Web 服务相关的结构模型,以及您可以如何使用IBM®Rational®Software Architect (RSA)在实际应用中实现这些模型。系列中的文章将使用实际的例子说明如何使用RSA以一种简单和可维护的方式捕捉 Web 服务的设计,并同时捕捉到最终工件需要的大部分外在细节。

本系列探讨了以下主题:

  • 第一部分——建模和产生 XML 模式。
  • 第二部分——建模和产生 WSDL。
  • 第三部分——将模式应用到 Web 服务工件的产生中。

您将从了解 XML 模式开始;这是因为,首先,单独使用WSDL要求信息以模式的形式描述,更为重要的是,信息——实际上所有数据——的设计应该是一个清楚和有效管理的活动,而不是接口设计的副产品。您在本文中看到的数据结构将作为WSDL信息的元素在下一篇文章中继续使用。

为什么为 Web 服务建立详细模型?

一个有趣的问题是,为Web 服务建立详细的模型是否有价值。毕竟,一个试图抽象掉不必要的细节以使您用一种在实现级不够清楚的方式理解系统的模型不是足够的吗?显然,在某些方面答案是肯定的,您希望提供一个下层实现工件的模型,而这种方法又允许您利用可视化语言的能力来简单地表达复杂的关系并逐级实现更大数据集合的表达。

这种为细节设计元素建模的方法在整体的模型驱动开发(MDD)方法中比较适用,在模型驱动开发方法中您可以定义多个互联的模型,用它们递进地表达问题或解决方案的精确的观点。这种方法如图1所示。模型的特定配置是具体到被模型化的服务的,因此不使用通常关联到对象管理组织(OMG)的模型驱动架构(MDA)的术语,包括计算无关,平台无关,以及平台相关的模型,等等。

图1. Web 服务开发的 MDD 配置
An MDD configuration for Web service development
An MDD configuration for Web service development

对配置元素的描述如下:

  • 分析模型:一个高度抽象的模型,包含了解决方案的代表元素,而不包含任何具体的平台或结构方面的考虑
  • 软件服务设计模型:一个从分析模型派生出来,应用了面向服务的架构方式的模型,但是不包含实际实现技术和平台
  • Web 服务实现模型:一个从软件服务设计模型派生出来的模型,Web 服务实现的特定细节被添加和提炼,以保证产生的工件被精确制定
  • XML或WSDL工件:实际可部署的工件,包括XML 模式和WSDL文档
  • 需求:每个模型的开发中使用的要求,同时还用来配置模型之间的转换

您会注意到有两种实现实际工件的方法:直接从软件服务模型实现或通过Web 服务模型实现。在第一种情况下会进行从更为抽象的软件服务模型的默认转换。尽管如此,如果需要对产生的工件的更多控制,那么可以使用更详细的Web 服务模型来更精确地定义目标。

建模 XML 模式

在 XML 模式的模型建立中——见参考资源,[3,4]——意识到一些相对“显而易见”的映射——从UML面向对象的世界观到XML 模式以文档为中心——是非常重要的,它们使您能够从一个简单的类模型自动产生出模式。不幸的是,这通常不是一个令人满意的经历,因为这些默认产生的模式元素无法与您手工建立的结构类型相比。因此,模型应以这样一种方式建立:产生的XML 模式能够被协调以准确的建立设计者的心中所想。

RSA 的 UML 到 XML 模式的转换

本文中使用的例子是用RSA开发的——见参考资源,[6]。版本 6.0.1引进了一组新的特性,包括下文介绍的 UML to XML schema 的转换。UML to XML schema 转换是RSA发布的一组模型到模型和模型到文本的转换之一,而且是用一个开放可扩展性的接口构建的,因此您或者第三方可以自行开发新的转换。

现在就让我们进入一个简单模型的示例,您希望为它建立一个XML 模式。为了完成任务,您将需要一张来自 XML 模式入门本身提供的购买订单表格(资源,[5]),如图2所示。

图2. 初始购买订单模型
The initial Purchase Order model
The initial Purchase Order model

右键点击包含购买订单元素的包来运行 UML to XSD 转换——在图上或模型浏览器中点击都可以。点击Transform > Run Transformation > UML to XSD,如图3所示。当您选择了一个转换,您将看到一个对话框,在这里您可以输入额外的转换选项。如果您是第一次运行转换,务必把包含您的模型的Eclipse项目设置为转换的目标。

图3. RSA 的 Transformation 菜单
The RSA Transformation menu
The RSA Transformation menu

为模型产生的模式与列表1近似。注意转换将产生一个警告,因为模型没有指定目标命名空间,而这是模式的一个要求。

列表1. 产生的购买订单模式
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:complexType name="purchaseOrder">
    <xsd:sequence>
      <xsd:element name="orderDate"/>
      <xsd:element name="shipTo" type="USAddress"/>
      <xsd:element name="billTo" type="USAddress"/>
      <xsd:element minOccurs="0" name="comment" type="string"/>
      <xsd:element name="items" type="Items"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Items">
    <xsd:sequence>
      <xsd:element maxOccurs="unbounded" minOccurs="0" name="item" type="Item"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="USAddress">
    <xsd:sequence>
      <xsd:element name="name" type="string"/>
      <xsd:element name="city" type="string"/>
      <xsd:element name="state" type="string"/>
      <xsd:element name="zip" type="string"/>
      <xsd:element name="country" type="string"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Item">
    <xsd:sequence>
      <xsd:element name="productName" type="xsd:string"/>
      <xsd:element name="quantity" type="xsd:positiveInteger"/>
      <xsd:element name="USPrice" type="xsd:decimal"/>
      <xsd:element name="comment" type="string"/>
      <xsd:element name="shipDate" type="xsd:date"/>
      <xsd:element name="partNum" type="SKU"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="SKU">
    <xsd:complexContent>
      <xsd:extension base="string">
        <xsd:sequence/>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>
</xsd:schema>

让我们比较产生的模式和入门中手工完成的版本,并探讨一些问题。首先,您需要添加命名空间,它由转换本身识别。为了完成这项工作您必须把一个UML profile 应用到您的模型中,该 profile 允许您通过使用UML的原型来提供额外信息,以很好地协调产生过程并捕捉到其它属性。选择包含您的模式元素的包以应用 profile 和原型,然后打开属性窗口和 profile 标签,如图4所示。

图4. 将 profile 应用于您的模型
Applying the profile to your model
Applying the profile to your model

点击Add profile 并在列表中选择 XSDProfile。这一操作将加载转换原型,那么选择相同的包并打开 Properties 窗口中的 Stereotypes 标签。一旦您添加了«schema»原型,滚动原型属性并向 targetNamespace 和 targetNamespacePrefix 属性中添加需要的值,如图5所示。

图5. «schema» 原型属性
The schema Stereotype Properties
The schema Stereotype Properties

这些操作将允许转换产生一个更为完整的XML <schema/>元素。但是在您重新运行转换之前,再添加一个项。在属性窗口中打开Documentation 标签并输入文本Purchase order schema for Example.com. Copyright 2000 Example.com. All rights reserved.。现在,重新运行转换并检查产生的模式——现在只需要看文件的最前面就可以了——如列表2所示。

列表2. 重新产生的模式
<xsd:schema xmlns:po="http://www.example.com/PO1" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            targetNamespace="http://www.example.com/PO1">
  <xsd:annotation>
    <xsd:documentation>Purchase order schema for Example.com.
    Copyright 2000 Example.com. All rights reserved.
    </xsd:documentation>
  </xsd:annotation>

正如您看到的,现在您不仅指定了目标的命名空间,而且还建立了一个命名空间值PO来限制模式和其中包含的来自示例的注解文档。

现在,您需要进行第二次变化,因为不是所有图1中显示的UML属性都能成为XML元素,如列表1所示。您需要其中一些UML属性作为XML属性被产生,因此您将再次使用 profile 来告知转换需要做什么;在这个例子中,您将需要选择属性并添加原型«attribute»,如图6中所示。在您完成属性选择以前,还需要把USAddress::country的属性类型改为XML 模式类型 NMTOKEN,并指定默认值US。要设置正确的类型,选择 Country 属性,打开属性的General 标签,并点击Select type。在产生的对话框中,滚动XSDDataTypes模型并选择需要的类型。

图6. 在模型中指定XML属性
Specifying XML attributes in the model
Specifying XML attributes in the model

但是,现在您又有了一个问题:SKU 类是作为一个复合类型产生的,您无法把它作为一个属性的类型(如果您回到示例模式中您会看到SKU是一个简单类型,有其指定的模式)。因此,把原型«simpleType»应用于SKU类,并把原型模式属性设置为\d{3}-[A-Z]{2}。另外,确保SKU类在XSDDataTypes模型中继承了字符串类,而不是继承了一个本地字符串类。这样属性和简单类型定义就完成了,如列表3中的片段所示。

列表3:指明变化的代码片段
...
<xsd:complexType name="USAddress">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="street" type="xsd:string"/>
      <xsd:element name="city" type="xsd:string"/>
      <xsd:element name="state" type="xsd:string"/>
      <xsd:element name="zip" type="xsd:string"/>
    </xsd:sequence>
    <xsd:attribute default="US" name="country" type="xsd:NMTOKEN"/>
  </xsd:complexType>
...
  <xsd:simpleType name="SKU">
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="\d{3}-[A-Z]{2}"/>
    </xsd:restriction>
  </xsd:simpleType>

正如您所看到的,您与入门中手工的购买订单模式示例更加接近了,但是还有以下两个区别:

  • 条目的复合类型中的 Anonymous 类型
  • 对全局元素声明的需要

先来看一下 Anonymous 类型,您需要向模型元素项中加入原型«complexType»。这看起来是多余的,因为默认的转换为模型类型创建了一个复合类型,但是请仔细观察原型的属性。为完成这个例子,需要把 Anonymous 属性的值设为True。您还需要为 Quantity 属性作相似的工作:您需要建立一个匿名简单类型。具体做法是,先建立一个新的类并将其命名为 Quantity (在本例中)。然后,在XSDDataTypes模型中建立一个到positiveInteger类的泛化关联。现在,在 原型化的属性 Quantity 上,设置anonymous=True以及maxExclusive=100,然后把 Item::quantity 的类型改为新的 Quantity 类,如图7所示。

图7. 指定 Quantity 匿名简单类型
Specifying the Quantity anonymous simple type
Specifying the Quantity anonymous simple type

这些更改的结果是一个对于条目复合类型更为完整的定义,如列表4所示。

列表4:项复合类型定义
<xsd:complexType name="Items">
    <xsd:sequence>
      <xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="productName" type="xsd:string"/>
            <xsd:element name="quantity">
              <xsd:simpleType>
                <xsd:restriction base="xsd:positiveInteger">
                  <xsd:maxExclusive value="100"/>
                </xsd:restriction>
              </xsd:simpleType>
            </xsd:element>
            <xsd:element name="USPrice"  type="xsd:decimal"/>
            <xsd:element ref="comment"   minOccurs="0"/>
            <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
          </xsd:sequence>
          <xsd:attribute name="partNum" type="SKU" use="required"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>

最后一个问题是,购买订单本身在入门中是作为一个全局元素声明的,属于全局复合类型。此外,comment 元素也是全局定义为字符串类型的。在模型中定义全局元素声明比您前面做的在模型中添加一些“标记”要复杂一些。这些变化如图8所示。

图8. 全局元素声明
Global Element Declarations
Global Element Declarations

要实现图8中的变化,您需要对模型作如下工作:

  • 将 purchaseOrder 重命名为 purchaseOrderType
  • 向 purchaseOrderType 中添加原型«complexType»(不一定是必须的,但是它使模型的意义更为明确)
  • 建立一个名为purchaseOrder 的新类,并加入原型«global»
  • 向 purchaseOrder 中添加一个属性,名字也是 purchaseOrder,原型是«element»并将类型指定为 purchaseOrderType
  • 建立一个名为 comment的新类,并加入原型«global»
  • 向 comment 中添加一个属性,名字也是 comment,原型是 «element» 并将类型指定为 XSDDataTypes::string
  • 将 Item::comment 的类型改为新建立的 comment 类
  • 将 Item::comment 的多重性从1改为0..1

所有这些改变的结果如列表5所示:产生的模式与原始例子在意义上是完全相同的,尽管文本的顺序可能有所不同。

列表5:现在产生的模式与基本示例的意义是相同的了
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:PO="http://www.example.com/PO1" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            targetNamespace="http://www.example.com/PO1">
  <xsd:annotation>
    <xsd:documentation>Purchase order schema for Example.com.
    Copyright 2000 Example.com. All rights reserved.
    </xsd:documentation>
  </xsd:annotation>
  <xsd:complexType name="USAddress">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="street" type="xsd:string"/>
      <xsd:element name="city" type="xsd:string"/>
      <xsd:element name="state" type="xsd:string"/>
      <xsd:element name="zip" type="xsd:string"/>
    </xsd:sequence>
    <xsd:attribute default="US" name="country" type="xsd:NMTOKEN"/>
  </xsd:complexType>
  <xsd:simpleType name="SKU">
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="\d{3}-[A-Z]{2}"/>
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:element name="purchaseOrder" type="PO:purchaseOrderType"/>
  <xsd:element name="element" type="xsd:string"/>
  <xsd:complexType name="purchaseOrderType">
    <xsd:sequence>
      <xsd:element name="shipTo" type="PO:USAddress"/>
      <xsd:element name="billTo" type="PO:USAddress"/>
      <xsd:element name="items" type="PO:Items"/>
    </xsd:sequence>
    <xsd:attribute name="orderDate" type="xsd:date"/>
  </xsd:complexType>
  <xsd:complexType name="Items">
    <xsd:sequence>
      <xsd:element maxOccurs="unbounded" minOccurs="0" name="item">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="productName" type="xsd:string"/>
            <xsd:element name="quantity">
              <xsd:simpleType>
                <xsd:restriction base="xsd:positiveInteger">
                  <xsd:maxExclusive value="100"/>
                </xsd:restriction>
              </xsd:simpleType>
            </xsd:element>
            <xsd:element name="USPrice" type="xsd:date"/>
            <xsd:element minOccurs="0" ref="PO:element"/>
            <xsd:element name="shipDate" type="xsd:date"/>
          </xsd:sequence>
          <xsd:attribute name="partNum" type="PO:SKU"/>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

一个更完整的例子

作为 使用 IBM 软件 Rational 产品 项目的一部分,我们开发了一个模型库,其中包含了很多 Web 服务家族的规范(通常称为WS-*)的表达。图9显示了目前已建立了模型的标准集合,同时也显示了使用UML «packageimport»关系能够指明模式间的依赖性。特别地,这些UML关系将产生XML 模式导入声明,使我们能够简单方便地为具体领域的模式建立模型,并把它们作为一组相关模式组件。

图9. WS-* 模式和依赖性
WS-* schema, and dependencies
WS-* schema, and dependencies

如果您把 WS-Addressing 模式作为这些模式中普遍出现的结构的代表来加以研究的话,您会发现它包含了一些从简单类型中派生出来的复合类型(我们在前面购买订单的例子中看到了简单类型的衍生)。其中两个复合类型还增加了额外属性:ServiceNameType::portName 和 Relationship::RelationshipType。更为有趣的是,所有四个新的复合类型都允许通配符属性,如图10所示。通过允许作者向标准类型中添加额外的属性来引进未来的扩展,这是 XML 模式的一个能力。要实现这一特性,使用«wildcard»和«attribute»原型的结合(现在属性名无关紧要了,但是按常规我们使用任何任何属性)。

图10. 从 XSD 类型衍生出的复合类型
complexTypes derived from XSD types
complexTypes derived from XSD types

接下来,观察模式引进的新复合类型。再次注意到 EndpointReferenceType 类型不仅允许通配符属性,还允许通配符元素(使用«wildcard»和«element»原型的组合)。您还会看到我们用 UML 多重性[0..1]将三个属性置为可选的,如图11所示。

图11. WS-Addressing 复合类型
WS-Addressing complexTypes
WS-Addressing complexTypes

最后,WS-Addressing 揭示了一组全局元素声明,标准期望用户复用这些声明。它们在图12中有所表示。

图12. 可复用元素声明
Reusable element declarations
Reusable element declarations

按上述步骤运行 UML to XSD 转换的结果如列表6所示。再一次地,我们可以将这一结果与W3C的标准模式进行比较。

列表6:从 UML to XSD 转换得到的结果模式
<?xml version="1.0"?>
<xs:schema targetNamespace="http://schemas.xmlsoap.org/ws/2003/03/addressing" 
           xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           elementFormDefault="qualified" 
           blockDefault="#all">
  <xs:element name="EndpointReference" type="wsa:EndpointReferenceType"/>
  <xs:complexType name="EndpointReferenceType">
    <xs:sequence>
      <xs:element name="Address" type="wsa:AttributedURI"/>
      <xs:element name="ReferenceProperties" 
			         type="wsa:ReferencePropertiesType" minOccurs="0"/>
      <xs:element name="PortType" type="wsa:AttributedQName" minOccurs="0"/>
      <xs:element name="ServiceName" type="wsa:ServiceNameType" minOccurs="0"/>
      <xs:any namespace="##other" processContents="lax" 
             minOccurs="0" maxOccurs="unbounded">
        <xs:annotation>
          <xs:documentation>
             If "Policy" elements from namespace 
             "http://schemas.xmlsoap.org/ws/2002/12/policy#policy" are used, 
             they must appear first (before any extensibility elements).
          </xs:documentation>
        </xs:annotation>
      </xs:any>			
    </xs:sequence>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
  </xs:complexType>
  <xs:complexType name="ReferencePropertiesType">
    <xs:sequence>
      <xs:any processContents="lax" 
                 minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="ServiceNameType">
    <xs:simpleContent>
      <xs:extension base="xs:QName">
        <xs:attribute name="PortName" type="xs:NCName"/>
        <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="Relationship">
    <xs:simpleContent>
      <xs:extension base="xs:anyURI">
        <xs:attribute name="RelationshipType" 
                         type="xs:QName" use="optional"/>
        <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="AttributedQName">
    <xs:simpleContent>
      <xs:extension base="xs:QName">
        <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:complexType name="AttributedURI">
    <xs:simpleContent>
      <xs:extension base="xs:anyURI">
        <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
  <xs:element name="MessageID" type="wsa:AttributedURI"/>
  <xs:element name="RelatesTo" type="wsa:Relationship"/>
  <xs:element name="To" type="wsa:AttributedURI"/>
  <xs:element name="Action" type="wsa:AttributedURI"/>
  <xs:element name="From" type="wsa:EndpointReferenceType"/>
  <xs:element name="ReplyTo" type="wsa:EndpointReferenceType"/>
  <xs:element name="FaultTo" type="wsa:EndpointReferenceType"/>
  <xs:element name="Recipient" type="wsa:EndpointReferenceType"/>
</xs:schema>

希望这个对 RSA 为 XSD 建立模型的能力的简要介绍能使您获得具体领域的模式,以及您的模型中的 Web 服务消息。更深一步,它应该为您提供了结合和分解模式的机会,而您使用的方式可能是用目前其它的模式编辑器很难可视化的。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational, SOA and web services, WebSphere
ArticleID=193703
ArticleTitle=Web 服务建模,第 1 部分: XML 模式
publish-date=02022007