了解 Web 服务规范: 第 3 部分:统一描述、发现和集成 (UDDI)

时间:2022-05-03 16:05:29

开始之前

本教程介绍统一描述、发现和集成 (UDDI)。UDDI 指定 Web 服务和其他公司信息的注册中心,旨在提供一种发现可使用的新 Web 服务并在理想的情况下实现操作自动化的方法。

本教程假定您熟悉 Web 服务和 SOAP 的一般概念。熟悉 WSDL 的相关知识也将有所帮助,但本教程中会对一般概念进行回顾。

为了理解本教程最后一部分给出的代码,您需要熟悉 Java,不过其中的概念与本教程的其他内容一样,适用于任何编程语言。

关于本系列

本系列教程以假想的报社 Daily Moon 为例,为了提高在竞争激烈的环境中的工作效率,其员工将使用各种 Web 服务来创建工作流系统,我们将在此过程中讲解各个 Web 服务基本概念。

第 1 部分说明了 Web 服务背后的基本概念,并演示了如何使用 SOAP(后续教程讨论的大部分内容的基础规范)来将 Classifieds Department 连接到内容管理系统。

第 2 部分进一步深入说明如何使用 Web 服务描述语言 (WSDL) 定义 Web 服务预期产生的消息,从而使团队更方便地创建服务以及连接到服务的客户机。

在第 3 部分中,团队希望准备一系列服务,并希望能方便地查找这些服务。与此对应,统一描述、发现和集成(Universal Description, Discovery and Integration,UDDI)提供了可用服务的可搜索注册中心,以便使自己的服务为其他人所注意。

第 4 部分和第 5 部分讨论 WS-Security 和 WS-Policy,将详细说明如何保证该报社的服务的安全,以及团队为了访问这些刚提供了安全保护的服务需要进行哪些更改。

第 6 部分重点讨论互操作性,因为必须从单个系统访问来自几个不同实现的服务。这一部分还将讨论 WS-I 证书中涉及的要求和测试。

最后,第 7 部分演示如何使用业务流程执行语言(Business Process Execution Language,WS-BPEL)来从各个服务创建复杂应用程序。

接下来让我们更为详细地了解一下本教程中将讨论的内容。

回页首

关于本教程

本系列的第 1 部分介绍了 Web 服务,并强调了 SOAP 的重要性。这些内容是通过虚构的 Daily Moon 报社的 Classifieds Department 进行相关工作的过程说明的。在本系列的第 2 部分中,Classifieds Department 决定创建自己的服务,并使用 Web 服务描述语言 (WSDL) 记录其使用方式。在第 3 部分中,报社的发行人决定全面推行 Web 服务,并强制要求使用 UDDI 注册中心来记录所有可用服务。

在本教程中,您将了解以下内容:

  • 什么是 UDDI
  • UDDI 的用途
  • UDDI 数据的结构
  • 如何有效地使用 UDDI 表示 WSDL
  • 可以对 UDDI 数据执行的操作
  • 使用 Java 与 UDDI 注册表交互

回页首

先决条件

为了处理本教程中的代码,您需要有以下软件:

Apache Geronimo 或其他 UDDI 实现。为了处理本教程中的代码,您需要访问 UDDI 注册中心。为了实现此目标,所使用的注册中心类型并不重要,因为 UDDI 根本就是 Web 服务,应该可以从任何平台或语言进行访问,并能够访问任何平台或语言。既然这样,就可以使用我们在本系列的第 1 部分和第 2 部分使用的 Apache Geronimo 应用服务器(同时也是 IBM 的 WebSphere Community Edition 的基础),其中预安装了 Apache 的 UDDI 注册中心实现 jUDDI,并将其配置为基本安装的一部分。您可以从 Apache.org 下载 Apache Geronimo。有关安装 Geronimo 的更多信息,请参见本系列的第 1 部分

UDDI4J——为了访问注册中心,本教程说明了 UDDI4J 项目的使用方法;此项目提供了 UDDI 概念和 Java 代码间非常紧密的集成。可以从 Sourceforge.net 下载 UDDI4J

Apache Axis2 或其他 SOAP 实现——可以手动创建 SOAP 消息,也可以手动对其进行解释,但手边如果有一个可用实现就会方便得多。您将使用的是 Apache Axis2,其中包含了各种 SOAP 相关的 API,可极大地简化您的工作。可以从 Apache.org 下载 Apache Axis2 。本教程使用的是 0.94 版,但应该也能使用更高版本。

Java 2 Standard Edition 的 1.4.x 版——所有这些工具都是基于 Java 的,本教程中将要构建的服务和客户机也是如此。可以从 Sun Developer Network 下载 J2SE SDK。Geronimo 将无法与 Java 1.5 一起正常使用。

另外,还需要 Web 浏览器和文本编辑器,但我想您已经有了这两个工具。如果愿意,还可以使用 Eclipse 之类的 IDE,但由于我们的重点是技术而不是工具,因此我将使用文本编辑器和命令行来编辑和编译文件。

概述

让我们看一看目前已经完成的工作。

已完成的工作

本系列教程逐步说明了虚构的 Daily Moon 报社的员工将其日常操作更改为基于 Web 服务的系统的过程。在第 1 部分中,Classifieds Department 通过与内容管理系统交互了解了 SOAP 的相关信息,在第 2 部分中,他们创建了自己的服务,并使用 Web 服务描述语言 (WSDL) 对其进行描述。现在,该报社的发行人 Rudy 对 Web 服务在其组织内的工作情况印象非常好,希望能更广泛地应用 Web 服务,甚至希望将其用于与其他组织进行交互。

为此,他要求 IT 人员 Gene 和 Francis 创建一个 Web 服务注册中心,以便能让其他部门更方便地找到为了将自己的操作过渡到 Web 服务而需要的构件。

Gene 和 Francis 认为最好的做法是实现统一描述、发现和集成 (UDDI) 注册中心,从而不仅能存储有关报社自己服务的信息,还能够与其他报社的注册中心进行交互。

回页首

WSDL 回顾

向 UDDI 注册中心注册服务的部分过程将涉及到对 WSDL 定义进行操作,因此在开始新项目前,Gene 重新分析了一下上个项目中的文件,以回顾 WSDL 的基本概念。请参见清单 1


清单 1. WSDL 文件
                    <wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema"        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"        xmlns:tns="http://www.daily-moon.com/classifieds"       xmlns:ns1="http://org.apache.axis2/xsd"        targetNamespace="http://www.daily-moon.com/classifieds"><wsdl:types>  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"          targetNamespace="http://org.apache.axis2/xsd"          elementFormDefault="unqualified"          attributeFormDefault="unqualified">    <xs:element name="createNewAdRequest">      <xs:complexType>        <xs:sequence>          <xs:element type="xs:string" name="content" />          <xs:element type="xs:string" name="endDate" />        </xs:sequence>      </xs:complexType>    </xs:element>    <xs:element name="createNewAdResponse">      <xs:complexType>        <xs:sequence>          <xs:element type="xs:int" name="newAdId" />        </xs:sequence>      </xs:complexType>    </xs:element>...  </xs:schema></wsdl:types><wsdl:message name="createNewAdRequestMessage">  <wsdl:part name="part1" element="ns1:createNewAdRequest" /></wsdl:message><wsdl:message name="createNewAdResponseMessage">  <wsdl:part name="part1" element="ns1:createNewAdResponse" /></wsdl:message><wsdl:message name="getExistingAdsResponseMessage">...</wsdl:message><wsdl:portType name="ClassifiedServicePortType">  <wsdl:operation name="createNewAd">    <wsdl:input message="tns:createNewAdRequestMessage" />    <wsdl:output message="tns:createNewAdResponseMessage" />  </wsdl:operation>  <wsdl:operation name="finalizeIssue">...</wsdl:operation>  <wsdl:operation name="editExistingAd">...</wsdl:operation>  <wsdl:operation name="getExistingAds">...</wsdl:operation></wsdl:portType><wsdl:binding name="ClassifiedServiceBinding"               type="tns:ClassifiedServicePortType">  <soap:binding transport="http://schemas.xmlsoap.org/soap/http"                 style="document" />  <wsdl:operation name="createNewAd">    <soap:operation soapAction="createNewAd" style="document" />    <wsdl:input>      <soap:body use="literal"                  namespace="http://ws.apache.org/axis2" />    </wsdl:input>    <wsdl:output>      <soap:body use="literal"                  namespace="http://ws.apache.org/axis2" />    </wsdl:output>  </wsdl:operation>  <wsdl:operation name="finalizeIssue">...</wsdl:operation>  <wsdl:operation name="editExistingAd">...</wsdl:operation>  <wsdl:operation name="getExistingAds">...</wsdl:operation></wsdl:binding><wsdl:service name="ClassifiedService">  <wsdl:port name="ClassifiedServicePort"              binding="tns:ClassifiedServiceBinding">    <soap:address location="http://www.daily-moon.com:8080/axis2/services/ClassifiedService" />  </wsdl:port></wsdl:service></wsdl:definitions>

由于篇幅的原因,我们从此文档中删除了部分代码,但我们仍然能通过剩下的部分了解其一般概念。

首先看最下面,我们定义了实际的服务,可通过特定的位置对其进行访问,且实现了 ClassifiedServiceBinding 接口。该接口表示为绑定,指定在 ClassifiedServicePortType portType 中定义的操作。portType 使用在 types 部分中定义的元素定义输入和输出消息。

XML 客户机可以读取此信息,并将其用于访问 Web 服务。(或者,更为准确地说,Web 服务客户机生成工具可以读取此信息,然后生成可以访问 Web 服务的客户机。)

回页首

我们的目标

在本教程中,Gene 和 Francis 将 Daily Moon 的信息拆分为 UDDI 友好的结构,包括业务信息和服务信息。他们将使用其通过 UDDI4J 将报社的信息和服务输入 UDDI 注册中心。它们还将与另一个报社 Daily Star 的私有注册中心协同工作,以查找提供即时体育赛事比分的服务。

现在让我们看一看到底什么是 UDDI。

UDDI 简介

UDDI 基本上就是一个 Web 服务,但与 SOAP 和 WSDL 存在很大差异,最好在开始之前了解一些背景知识。

为何使用 UDDI?

当所有应用程序都位于本地时,要找到所需的功能会非常容易。不过,使用 Web 服务之类的分布式系统时,您不能获得*注册中心的好处。分布式系统也容易发生更改。而这正是 UDDI 的用武之地。它旨在用于两个目的。最初形成时,它被认为是一种“通用业务注册中心”。其想法是,企业可以使用以下三种方法之一搜索合作伙伴:

  • “白页”:白页与电话簿中用于查找公司信息的白页类似。例如,如果您知道公司的名称,可以在其中查找公司的地址、如何进行联系,甚至还能够确定与组织中的哪个人联系。
  • “黄页”:同样,黄页与电话簿中的黄页一样,可以在其中根据分类查找公司。UDDI 指定了各种分类法,以供各个公司用于对自己进行分类。例如,如果您在查找体育用具,则可以查找其北美工业分类系统(North American Industry Classification System,NAICS)代码为 339920 的公司。
  • “绿页”:电话簿中没有绿页,但这里的想法是,公司可以使用此搜索方法来查找实现了特定服务的贸易合作伙伴。例如,可以搜索实现了使用邮政编码的距离计算功能的公司。

UDDI 同时也被认为是一种保持分布式应用程序长期运行的方法。其想法是这样的,可以缓存有关访问特定服务的信息,如果客户机崩溃,应用程序将自动回到注册中心并进行检查,以确定信息是否已更改。如果已更改,则可以直接在应用程序内进行更改(在理想的情况下将自动进行更改)并重试您的请求。

回页首

UDDI 的实际应用

当 Francis 想尝试统一业务注册中心(Universal Business Registry,UBR)时,发现它已不复存在了。事实上,在推出 UDDI 3.0 版时,UBR 已被其企业赞助商终止了。为什么呢?无论 UBR 的想法多么好,但事实是基本上没有人使用它。其原因是双方面的:首先,在 UBR 中几乎没有任何可用信息——除非希望使用的是实时股票行情服务(在此情况下可找到大量信息)。第二个原因是,通常公司并不会与不熟悉的组织开展业务。很少有组织通过自动搜索寻找贸易伙伴,然后在不进行大量前期工作的情况下直接向其开发系统。自动搜索和调用模型并没有实际的效用。

同时,这也受到 UDDI 在某种意义上有些超前的事实的阻碍。它设计为处理所有类型的服务,而不仅是 SOAP over HTTP,并没有提供使用 WSDL 定义的机制(至少尝试使用它的人这样认为)。

不过,这并不是说根本不会使用 UDDI。相反,实际上它更多地用作“私有注册中心”。各个公司创建自己的注册中心,并在其中输入自己的服务。UDDI 还可以用作内部的业务注册中心,就像 LDAP 的传统用法一样。

而且,随着从“只有 Web 服务”的思维方式转变为更多强调面向服务的体系结构(Service Oriented Architecture,SOA)的思维方式,这种处理 SOAP 之外的内容的能力可能具有极高的长期价值。

回页首

UDDI 数据结构

UDDI 注册中心中的信息包含五种不同类型:

  • businessEntity 或实际企业组织。这可以是整个组织,也可以为隶属部门或分支机构。
  • publisherAssertion 或各个 businessEntities 间的关系。publisherAssertions 必须由双方共同声明才有效(因此无法将自己声明为另一个公司的分支机构),除非两个实体都对发布者负责,或除非两个实体都由同一个用户帐户输入注册中心。
  • bindingTemplate,这对服务接口的规范非常重要。它可以由多个 businessServices 进行实现。
  • businessService 或业务提供的服务。虽然现在看起来有些简单,但在 UDDI 的世界中,这并不意味着它是 Web 服务。例如,可以实际将您的公司的电话服务支持(即用户实际拨打的电话号码以及其他相关的内容)指定为 UDDI 服务。当然,您并不会提供虚拟 WSDL 文档,但这将是您的公司提供的一个服务。
  • tModels 或元数据模型。研究了 UDDI 后,Francis 得出结论,tModel 可能是 UDDI 未达到预期目标的最大原因。作为服务的注册中心,您希望找到像 WSDL 一样直接为服务指定接口的方法。但是,正如所指出的,UDDI 并不是仅供用于 Web 服务,而是设计时包含了更大的灵活性。tModels 的确可帮助指向 XML 文档(我们稍后将证实这一点),但事实上旨在提供通过服务、业务或任何其他对象指定关于某个事项的信息的通用方法。

以上是 UDDI 的基本数据结构。现在让我们了解一下可以用于与 UDDI 交互的方法。

回页首

UDDI API

UDDI 被认为过于复杂,但其核心在于上述五种数据类型以及四个操作:findgetsave  delete。也就是说,UDDI 规范说明了可以对数据进行的以下操作:

  • find_xxfind_businessEntityfind_businessService 等方法提供了在 UDDI 注册中心中搜索记录的方法。这些方法返回用于标识对象的键。
  • get_xx:获得了标识对象的唯一键后,可以使用 get_businessService  get_xx 方法来检索实际的对象本身。例如,get_businessService 将返回整个 businessService 对象,可从其中获得任何所需的信息。
  • save_xx:您可能已经猜到,这些方法会将信息添加到数据库,或更改数据库中已经存在的信息。
  • delete_xxdelete_bindingTemplatedelete_tModel 等方法获取对象的唯一键作为参数,然后将其从数据库中删除。这些方法的实际行为根据所删除的对象不同而有所变化。例如,由于数据库中的其他对象经常引用 tModels,因此并不能实际将其删除,而会将其隐藏。

几乎所有的 UDDI 都依赖于五个对象(businessServicebindingTemplatepublisherAssertionbusinessEntity  tModel)与四个操作(findgetsave  delete)的这 20 个交叉方法。

现在让我们看一看此结构的实际情况。

UDDI 数据结构

为了开始将公司添加到 UDDI 注册中心,Francis 定义了要输入注册中心的信息。

businessEntity

第一步是将报社本身的信息输入注册中心。为此,Francis 创建了一个 businessEntity 对象。此对象包含有关企业的信息,既用于对企业进行标识,也用于提供与其有关的信息。例如,businessEntity 对象可以包含多个名称(从而能够为公司所使用的每种语言创建一个 name 元素),联系信息(Francis 被告知将 Pat Mooney 添加为主要联系人)、与实体相关的服务(Gene 将稍后添加)和用于对公司进行标识与分类的信息。

尽管大部分人都不会采用这种方式与 UDDI 注册中心交互,但要注意的一个事实是,UDDI 实际上就是 SOAP 应用程序;执行的所有操作都作为往返传递 SOAP 消息的结果发生。这意味着,Francis 所创建的 businessEntity 最终将表示为 XML 元素。请参见清单 2


清单 2. businessEntity 元素
                                        <businessEntity businessKey="{uuid}" operator="(optional)" authorizedName="(optional)"><discoveryURLs><discoveryURL></discoveryURL><discoveryURL></discoveryURL></discoveryURLs><name>The Daily Moon</name><name lang="fr-ca">La Lune Quotidienne</name><description></description><contacts><contact><description></description><personName>Pat Mooney</personName><phone></phone><email>patm@daily-moon.com</email><address useType="optional, billing, etc." sortCode="optional" tModelKey="optional"><addressLine></addressLine><addressLine></addressLine><addressLine></addressLine></address></contact><contact>         ...</contact></contacts><businessServices><businessService serviceKey="required" businessKey="optional">         ...</businessService></businessServices><identifierBag>      ...</identifierBag><categoryBag>      ...</categoryBag></businessEntity>                

businessEntity 记录并不一定要很复杂。事实上,按照 businessKey 的要求,单个名称和一个唯一标识符就足够了。不过,出于完整性考虑,我包含了完整的结构,但去掉了一些稍后将另行讨论的部分。在生产环境中,应将空白元素删除。

让我们看一看此处的一些信息。

从代码的起始处开始,businessKey 当然是唯一标识符。operator 和 authorizedName 属性由注册中心内部进行处理;您不必考虑这部分内容,它们仅仅分别表示运行注册中心的实体和添加此记录的人员或帐户。

discoveryURLs 会在每次将 businessEntity 保存到注册中心时添加到注册中心。这些 URL 旨在用作供用户访问有关 businessEntity的更多信息的地址。

请注意,Francis 添加了一个额外的名称,用于以法语表示其名称(报社打算在法国宣传其业务)。这些实体中的每一个都具有 lang属性,用于指定相关语言的代码。您可以提供常见缩写的可替换名称,如 IBM 对应于 International Business Machines。

接下来的可选描述是一个简单的文本字符串。事实上,UDDI 中的大部分元素都具有一个可选描述。

接下来,Francis 添加了其他一些联系人。对于每个联系人,他可以根据需要指定任意多的信息。通常会提供 personName,但电话号码、电子邮件等其他信息是由个人自行提供的,因为 UDDI 注册中心的所有信息对所有用户均可用。联系人还可能具有地址信息,使用 useType 来指定地址类型。例如,对于联系人,它可以为家庭地址和公司地址等。对于公司,useType 可以为总部、发行中心等等。

businessServices 元素将列出已与 businessEntity 关联的任何服务。由于 Gene 尚未添加任何服务,故而此元素通常为空。最后两项 identifierBag  categoryBag 提供了有关公司本身的其他信息,以便进行搜索。我们稍后将对二者分别进行分析。

回页首

publisherAssertion

publisherAssertion  businessEntity 之间的关系声明。例如,Francis 知道此项目的目标之一是使用 Daily Star 中提供的服务,其中,Daily Star 将为 Daily Moon 提供体育赛事比分。这使得 Daily Star 成为 Daily Moon 的内容提供者。Francis 可以按照清单 3 中所示的方式表示此关系。


清单 3. publisherAssertion 
                    <publisherAssertion><fromKey>(businessKey for Daily Star)</fromKey><toKey>(businessKey for Daily Moon)</toKey><keyedReference tModelKey="904BD800-D53A-11DA-B055-850A1DA99D79"          keyName="optional" keyValue="CONTENTPROVIDER" /></publisherAssertion>

fromKey  toKey 元素表示所涉及的两个实体的唯一 ID。在本例中,这两个实体指 Daily Moon  Daily StarDaily Star  Daily Moon 的内容提供者,故而据此放置二者的键。keyedReference 将会多次出现。在本例中,我们可以说引用本身就是内容提供者。keyValue  keyedReference 的唯一必需属性,但常常也会包含 keyName(为了提高可读性)和 tModelKey(标识特定的引用类型)。在本例中,tModelKey 表示两个报社之间关联的命名空间(与内部关系相对)。我们稍后将对 tModels 进行进一步的讨论。


businessService

接下来,Francis 需要输入有关 Daily Moon 将要提供的任何实际服务的信息。除了所需的唯一 serviceKey 和名称外,businessService 对象中还包含两条重要信息。即 bindingTemplates 和服务所属的任何类别。请参见清单 4


清单 4. businessService 
                                        <businessService serviceKey="required" businessKey="optional"><name></name><description></description><bindingTemplates><bindingTemplate serviceKey="optional" bindingKey="required">         ...</bindingTemplate></bindingTemplates><categoryBag>      ...</categoryBag></businessService>                


BindingTemplate

Tmodelinstancedetails 指示其兼容的应用程序或规范。请参见清单 5


清单 5. bindingTemplate 
                                        <bindingTemplate serviceKey="optional" bindingKey="required">                    <accessPoint> OR <hostingRedirector bindingKey="another binding to be used instead"><tModelInstanceDetails><tModelInstanceDetail><tModelInstanceInfo tModelKey=""uuid key for specification tModel">><description></description><instanceDetails><description></description><overviewDoc><description></description><overviewURL></overviewURL></overviewDoc><instanceParams>string</intstanceParms></instanceDetails></tModelInstanceInfo><tModelInstanceInfo>            ...</tModelInstanceInfo>  </tModelInstanceDetail>   <tModelInstanceDetails></bindingTemplate>TModels (including tModel of tModels)Serve two purposes.  As specification indicators, and as namespace indicators.<tModel tModelKey="required" operator="optional" authorizedName="optional"><name></name><description></description><overviewDoc><description></description><overviewURL></overviewURL><overviewDoc>   <identifierBag>...</identifierBag><categoryBag>      ...</categoryBag></tModel>                

bindingTemplate 定义在何处能找到服务以及其执行什么工作,这听起来并不复杂。不过,这两项功能在 UDDI 上下文中担任了新的重要角色。例如,为了确定可以在何处访问服务,可以提供 accessPoint(表示简单的 URL)或任何其他相应的数据,如求助热线的号码或 hostingRedirector

分类法

对信息进行分类时,如果能就如何进行分类达成一致则很有帮助。这些一致同意的分类标准称为分类法。

例如,北美工业分类系统 (NAICS) 通过为每种类别指定一个唯一代码来区分“体育用品出租”、“体育用品商店”和“二手体育用品商店”。这样,当遇到代码 532292 时,就知道所说的是出租体育用品的商家,而不是出售这些东西的商家。

UDDI 操作员向系统添加一系列分类法作为例行规定,可供您在无需为其注册 tModels 的情况下使用。例如,jUDDI 可以使用 tModel  A035A07C-F362-44DD-8F95-E2B134BF43B4  uddi-org:general_keywords 创建tModel,因此您可以使用它,而不必自己定义。

当服务的绑定发生更改时,将使用 hostingRedirector。例如,如果Daily Moon 要建立自己的分类广告服务,并允许这两家报社使用createNewAdd() 调用提交广告,则需要确保服务永远不会更改,或者提供一个方法来告知客户机服务已发生更改。而这就是要使用hostingRedirector 的地方。在这种情况下,Francis(或实际由 Gene 进行)将创建一个新 bindingTemplate,用于表示新信息,并将该bindingTemplate  bindingKey 输入 hostingRedirector 元素的bindingKey 属性中。

定义服务能进行的工作可能会更困难。这个原因是两方面的。首先,由于我们讨论的并不一定是 SOAP 服务,因此无法直接为其提供 WSDL 文件。其次,由于这应该是一个自动化过程,因此需要以无歧义的方式提供此信息。

这样所得的结果是一个包括 tModelInstanceDetails 元素的bindingTemplate,而此元素中又包含一个或多个tModelInstanceDetail 元素。每个 tModelInstanceDetail 元素又包含一个或多个 tModelInstanceInfo 元素,而后者各自指向一个特定的tModel。我们稍后将对 tModels 进行讨论。不过,重要的是要理解,tModelInstanceInfo 元素可以包含一个 instanceDetails 元素,后者本身包含一个 overviewDoc,其中包括 overviewURL,即实际定义服务的文档的 URL。(是的,就是在此处指定 WSDL 文件。)


TModels(包括 tModels  tModel

现在让我们讨论一下 tModels

tModels 实际有两个用途。第一,就是我们在关于 publisherAssertions 的讨论中了解到的,作为“命名空间指示符”。也就是说,tModel 可以提供用于区分很容易混淆的信息类型的方法。经常给出的一个例子是税码(或其他很容易混淆的数字)。例如,包含以下内容的 keyedReference 并不会提供所有信息,因为这两个税务 ID 属于不同的国家。它们在两个国家里都称为税务 ID,但就编程而言,我们必须提供一个区分二者的方法,如清单 6 中所示。


清单 6. 来自不同国家的税务 ID
                    <keyedReference tModelKey="" keyName="taxid" keyValue="11111111" /><keyedReference tModelKey="" keyName="taxid" keyValue="22222222" />

这就是要使用 tModel 的地方。我们可以为“美国税码”创建一个键模型,再为“墨西哥税码”创建一个键模型,依此类推。创建了这些模型后,可以使用其 tModel 键来进一步限定这些 keyedReference。请参见清单 7


清单 7. tModels  keyedReferences 
                    <tModel tModelKey="902CDE50-D53A-11DA-B055-A74C17FA61A7">   <name>United States codes</name></tModel><tModel tModelKey="062377D0-D5F5-11DA-8170-8ACF057FECAD">   <name>Mexico codes</name></tModel><keyedReference tModelKey="902CDE50-D53A-11DA-B055-A74C17FA61A7"                 keyName="taxid" keyValue="11111111" /><keyedReference tModelKey="062377D0-D5F5-11DA-8170-8ACF057FECAD"                 keyName="taxid" keyValue="22222222" />

tModel 的另一个用途是作为规范标识符。例如,Francis 知道 Gene 将为上个月所处理的 ClassifiedService 创建相应的条目。此服务表示特定的接口,因此可以使用 tModel 对其进行标识。tModel 可能与清单 8 中所示类似。


清单 8. tModel 
                                        <tModel tModelKey="66999A50-D5F4-11DA-9A50-FA44D6AD622A"         operator="optional" authorizedName="optional">   <name>Classified Service Interface</name>   <description></description>   <overviewDoc>      <description></description>      <overviewURL>        http://www.daily-moon.com/ClassifiedService-interface.wsdl      </overviewURL>   <overviewDoc>   <identifierBag>...   </identifierBag>   <categoryBag>      ...   </categoryBag></tModel>                

和我们所讨论的各个其他对象一样,tModel 具有一个唯一键,这是它唯一必需的信息。不过,在本例中,我们还要指定overviewURL,即接口的 WSDL 文件的 URL。(我们将在后面部分创建它。)

同样,可以使用 identifierBag  tModels 进行标识,还可以使用 categoryBag 对其进行归类。接下来让我们看一看如何进行此工作。

回页首

identifierBag

由于 UDDI 设计十分灵活,因此没有特定的方式来标识 businessEntity。UDDI 并不强制要求使用其 D-U-N-S 编号、其税务 ID 编号或任何特定的标识方法对公司进行标识。相反,它提供了一个容器,可以根据需要在其中放置任意数量的标识符,如清单9 中所示。


清单 9. indentifierBag 
                                        <identifierBag>                    <keyedReference tModelKey="4064C064-6D14-4F35-8953-9652106476A9"          keyName="DUNS Number" keyValue="55555555" />                    <keyedReference tModelKey=""          keyName="US Tax ID" keyValue="111111111" /></identifierBag>                

 identifierBag 中添加的每个 keyedReference 都代表用户在搜索中找到相关对象的另一个机会。

回页首

categoryBag

categoryBag 的工作方式与 identifierBag 类似,因为其中包含一个或多个 keyedReferences。请参见清单 10


清单 10. categoryBag 
                                        <categoryBag>   <keyedReference tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"            keyName="optional" keyValue="511110" /></categoryBag>                


在本例中,Francis 创建了一个   categoryBag,指示 Daily Moon 是一个报社分支机构。现在让我们了解如何使用所有这些对象。

将 WSDL 映射到 UDDI

在 Gene 编写任何代码将所有这些信息发送到 UDDI 注册中心前,他必须在其当前服务和 UDDI 之间建立一个重要的联系。他必须对其进行调整,以适应 UDDI 的服务、接口和绑定模板概念。

服务与接口

您可能还记得,服务在 UDDI 中包含三个部分:接口(使用 tModels 定义)、businessService  bindingTemplate(用于将两者捆绑在一起)。因此,Gene 将首先确定用于定义 SOAP 服务的 WSDL 文件如何适应此结构。

他首先对 WSDL 进行分析。幸运的是,这非常简单。除了实现本身外,其余所有 WSDL 内容都属于接口。请参见清单 11


清单 11. WSDL 接口
                    <wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema"       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"       xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"       xmlns:tns="http://ws.apache.org/axis2"      xmlns:axis2="http://ws.apache.org/axis2"      xmlns:ns1="http://org.apache.axis2/xsd"       targetNamespace="http://ws.apache.org/axis2">   <wsdl:types>      <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"             targetNamespace="http://org.apache.axis2/xsd"             elementFormDefault="unqualified"             attributeFormDefault="unqualified">         <xs:element type="ns1:ClassifiedAd" name="ClassifiedAd" />         <xs:complexType name="ClassifiedAd">            <xs:sequence>               <xs:element type="xs:int" name="id" />               <xs:element type="xs:string" name="content" />               <xs:element type="xs:string" name="endDate" />               <xs:element type="xs:string" name="startDate" />            </xs:sequence>         </xs:complexType>         ...      </xs:schema>   </wsdl:types>   <wsdl:message name="createNewAdRequestMessage">      <wsdl:part name="part1" element="ns1:createNewAdRequest" />   </wsdl:message>   <wsdl:message name="createNewAdResponseMessage">      <wsdl:part name="part1" element="ns1:createNewAdResponse" />   </wsdl:message>   ...   <wsdl:portType name="ClassifiedServicePortType">      <wsdl:operation name="finalizeIssue">         <wsdl:input message="tns:finalizeIssueRequestMessage" />      </wsdl:operation>      <wsdl:operation name="createNewAd">         <wsdl:input message="tns:createNewAdRequestMessage" />         <wsdl:output message="tns:createNewAdResponseMessage" />      </wsdl:operation>      ...   </wsdl:portType>   <wsdl:binding name="ClassifiedServiceBinding"                   type="tns:ClassifiedServicePortType">      <soap:binding transport="http://schemas.xmlsoap.org/soap/http"                   style="document" />      <wsdl:operation name="createNewAd">         <soap:operation soapAction="createNewAd" style="document" />         <wsdl:input>            <soap:body use="literal"                         namespace="http://ws.apache.org/axis2" />         </wsdl:input>         <wsdl:output>            <soap:body use="literal"                         namespace="http://ws.apache.org/axis2" />         </wsdl:output>      </wsdl:operation>      ...   </wsdl:binding></wsdl:definitions>

这包括类型定义(XML 模式)、任何消息、portType 甚至 binding,因为绑定并不是特定于实现的。

实现由实际的服务元素组成。请参见清单 12


清单 12. 服务实现 WSDL
                    <wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema"       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"       xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"       xmlns:tns=        "http://www.daily-moon.com/ClassifiedService-interface"      xmlns:axis2="http://ws.apache.org/axis2"      xmlns:ns1="http://org.apache.axis2/xsd"       targetNamespace=        "http://www.daily-moon.com/ClassifiedService-interface"><wsdl:import namespace="http://ws.apache.org/axis2"    location="http://www.nicholaschase.com/ClassifiedService-interface.wsdl" /><wsdl:service name="ClassifiedService">   <wsdl:port name="ClassifiedServicePort"                binding="axis2:ClassifiedServiceBinding">      <soap:address          location= "http://www.daily-moon.com:8080/axis2/services/ClassifiedService"       />   </wsdl:port></wsdl:service></wsdl:definitions>

请注意,实现文件导入了接口文件。也就是说,此文件是服务的完整定义。

现在让我们来看一看这如何与我们的 UDDI 对象对应。

回页首

将接口映射到 tModel

Gene 首先必须将接口映射到相应的 tModel。请参见清单 13


清单 13. tModel 
                    <tModel tModelKey="66999A50-D5F4-11DA-9A50-FA44D6AD622A">   <name>http://www.daily-moon.com/ClassifiedService-interface</name>   <description>Interface for the Daily Moon Classified Department web application</description>   <overviewDoc>      <description>WSDL interface document</description>      <overviewURL>          http://www.daily-moon.com/ClassifiedService-interface.wsdl      </overviewURL>   <overviewDoc>   <categoryBag>      <keyedReference tModelKey="C1ACF26D-9672-4404-9D70-39B756E62AB4"               keyName="uddi-org:types" keyValue="wsdlSpec" />      <keyedReference tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"                keyName="ntis-gov:naics:1997" keyValue="511110" />   </categoryBag></tModel>

首先看最下面,请注意 categoryBag。Gene 添加了两个 keyReferences。第一个指定我们事实上在处理 WSDL 文件。已预定义了特定的 tModelKey 来表示 uddi-org:types 命名空间。所有 WSDL 文件都必须采用这种方式指定。第二个引用绑定到 ntis-gov:naics:1997 命名空间,因此 Gene 的用户可以方便地在表示与报纸有关的服务的所有 WSDL 文件中找到此定义。继续往上,我们可以看到 overviewDoc。除了可选描述外,它还指定了实际的 URL,可以在此处找到 WSDL 文件。

最后,tModel 的名称应与 WSDL 文件的目标命名空间匹配。

回页首

将实现映射到绑定模板

Gene 接下来必须将实现本身映射到 bindingTemplate。请参见清单 14


清单 14. bindingTemplate 
                    <bindingTemplate serviceKey="" bindingKey="">   <accessPoint>      http://www.daily-moon.com:8080/axis2/services/ClassifiedService   </accessPoint>   <tModelInstanceDetails>      <tModelInstanceDetail>         <tModelInstanceInfo                  tModelKey="66999A50-D5F4-11DA-9A50-FA44D6AD622A">            <instanceDetails>               <description>The canonical implementation of                   the Daily Moon's classified service.</description>               <overviewDoc>                  <overviewURL>           http://www.daily-moon.com/ClassifiedService-impl.wsdl                  </overviewURL>               </overviewDoc>            </instanceDetails>         </tModelInstanceInfo>      </tModelInstanceDetail>   </tModelInstanceDetails></bindingTemplate> 

这里我们看到,Gene 创建了一个新 bindingTemplate,并将 accessPoint 设置为实际服务的 URL。键 modelInstanceDetails 将接口tModel(通过 tModelKey)链接到实现文档(在 overviewDoc 中指定)。现在他只需要从服务引用它即可。

回页首

 businessService  businessEntity 引用 bindingTemplate

最后一步是将所有这些组装起来,并从 businessService 引用 bindingTemplate、从 businessEntity 引用 businessService。请参见清单 15


清单 15. 完整的 businessEntity 
                    <businessEntity businessKey="1A3DB880-D5F4-11DA-B880-F94D3591C691">   <name>The Daily Moon</name>   <name lang="fr-ca">La Lune Quotidienne</name>   <contacts>      <contact>         <personName>Pat Mooney</personName>         <email>patm@daily-moon.com</email>      </contact>   </contacts>   <businessServices>      <businessService             serviceKey="064B4170-D5F5-11DA-8170-A74C17FA61A7">         <name>ClassifiedService</name>         <bindingTemplates>            <bindingTemplate                  bindingKey="904BD800-D53A-11DA-B055-850A1DA99D79">               <accessPoint>      http://www.daily-moon.com:8080/axis2/services/ClassifiedService               </accessPoint>               <tModelInstanceDetails>                  <tModelInstanceDetail>                     <tModelInstanceInfo                  tModelKey="66999A50-D5F4-11DA-9A50-FA44D6AD622A">                        <instanceDetails>                           <description>The canonical implementation of the Daily Moon's classified service.</description>                           <overviewDoc>                              <overviewURL>           http://www.daily-moon.com/ClassifiedService-impl.wsdl                              </overviewURL>                           </overviewDoc>                        </instanceDetails>                     </tModelInstanceInfo>                  </tModelInstanceDetail>               </tModelInstanceDetails>            </bindingTemplate>         </bindingTemplates>         <categoryBag>            <keyedReference                  tModelKey="C1ACF26D-9672-4404-9D70-39B756E62AB4"                 keyName="uddi-org:types" keyValue="wsdlSpec" />            <keyedReference                  tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"                  keyName="ntis-gov:naics:1997" keyValue="511110" />         </categoryBag>      </businessService>   </businessServices>   <identifierBag>      <keyedReference tModelKey="4064C064-6D14-4F35-8953-9652106476A9"          keyName="DUNS Number" keyValue="55555555" />      <keyedReference tModelKey=""          keyName="US Tax ID" keyValue="111111111" />   </identifierBag>   <categoryBag>      <keyedReference tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"            keyName="optional" keyValue="511110" />   </categoryBag></businessEntity>


也就是说,服务现在已经完成:Gene 现在有了一个包含服务的   businessEntity

查找和使用信息

现在已经有了所有信息(至少从理论上是这样),Gene 需要再次对查找的过程进行处理。

UDDI 搜索的工作方式:查找公司

在 UDDI 中进行搜索将涉及到两个步骤。首先,查找所需对象的键,然后获取有关该对象的信息。因此,为了查找公司,Gene 需要创建 find_business 请求,如清单 16 中所示。


清单 16. 按类别查找公司
                    <find_business generic="2.0" xmlns="urn:uddi-org:api_v2" >   <categoryBag>      <keyedReference tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"            keyName="optional" keyValue="511110" />   </categoryBag></find_business>

此请求将查找所有与 categoryBag 中引用的类别匹配的公司。对 categoryBag 值的搜索被作为“AND”搜索对待。也就是说,任何匹配的项至少属于一个要匹配的类别。您还可以通过这样的方式指定 identifierBag 搜索。不过,Gene 并非真的对按类别搜索感兴趣。他只是暂时希望知道公司的信息是否位于注册中心中,因此,他完全可以采用按名称搜索的方式,如清单 17 中所示。


清单 17. 按名称查找公司
                    <find_business generic="2.0" xmlns="urn:uddi-org:api_v2" >   <name>daily</name></find_business>

请注意,名称搜索将以像所查找的名称后面跟有通配符一样的方式处理。也就是说,此处的搜索与 "daily*" 搜索一样,因此 Daily Moon  Daily Star 以及其他以“daily”开头的公司都将被列出。这些搜索还不区分大小写。

回页首

搜索的结果

find_business 请求将返回一个 businessList。请参见清单 18


清单 18. 搜索返回的公司
                    <businessList generic="2.0" operator="uddi.sourceOperator"     truncated="true" xmlns="urn:uddi-org:api_v2">   <businessInfos>      <businessInfo             businessKey="1A3DB880-D5F4-11DA-B880-F94D3591C691" >          <name>The Daily Moon</name>          <serviceInfos>             <serviceInfo serviceKey=                     "064B4170-D5F5-11DA-8170-A74C17FA61A7">                <name>ClassifiedService</name>             </serviceInfo>          </serviceInfos>      </businessInfo>      <businessInfo>         ...      </businessInfo>   </businessInfos></businessList>

对于满足搜索条件的每个 businessEntitybusinessList 都包含一个对应的 businessInfo 元素。其中的每个 serviceInfo 元素都包含实际的 businessEntity  businessKey。其中还包含将在 businessEntity 中列出的信息的缩略版,以便您确定其是否是您所要查找的公司。

获得了相应的 businessKey 后,需要获得有关 businessEntity 的更多信息。

回页首

获得了键后的后续操作

要基于 businessKey 获得有关特定 businessEntity 的更多信息,需要执行 get_businessDetail 请求。请参见清单 19


清单 19. 获取公司信息
                    <get_businessDetail generic="2.0" xmlns="urn:uddi-org:api_v2" > <businessKey>1A3DB880-D5F4-11DA-B880-F94D3591C691</businessKey></get_businessDetail>

可以在 get_businessDetail 请求中列出任意多的 businessKey。而后者在结果中均有自己对应的 businessDetail 元素。请参见清单 20


清单 20. 返回的公司详细信息
                    <businessDetail generic="2.0" operator="uddi.sourceOperator"     truncated="false" xmlns="urn:uddi-org:api_v2">   <businessEntity businessKey="1A3DB880-D5F4-11DA-B880-F94D3591C691">      <name>The Daily Moon</name>      <name lang="fr-ca">La Lune Quotidienne</name>      <contacts>...         <keyedReference               tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"               keyName="optional" keyValue="511110" />      </categoryBag>   </businessEntity> </businessDetail>

每个 businessDetail 元素均包含所涉及公司的完整 businessEntity

回页首

更改搜索工作方式

虽然这可以正常工作,但有时候会希望更改搜索的行为。例如,或许您希望查找精确匹配,而不是通配符匹配,或者可能希望进行区分大小写的搜索。您可以使用 findQualifiers 来更改搜索的行为。请参见清单 21


清单 21. 使用 findQualifiers 
                    <find_business generic="2.0" xmlns="urn:uddi-org:api_v2" >   <findQualifiers>      <findQualifier>sortByNameAsc</findQualifier>   <findQualifiers>   <name>daily</name></find_business>

在这种情况下,搜索将会按通常的方式进行,但返回的结果将按照名称进行排序。可以添加任意多的 findQualifier 元素。其中可用的有 exactNameMatchcaseSensitiveMatch, sortByNameDesc, sortByDateAsc  sortByDateDesc,或者 LikeKeys(告知注册中心,如果 categoryBag  identifierBag 中的键共享相同的 tModelKey,则将其作为“OR”条件对待),以及combineCategoryBags(无论实际属于 businessEntitybusinessService  bindingTemplate,都将 businessEntity 的所有categoryBags 作为属于 businessService 的对象处理)。

回页首

查找某种类型的服务

Gene 还希望确保用户可以基于特定的接口查找任何服务。例如,以下查询将查找使用 ClassifiedService 接口构建的bindingTemplates。请参见清单 22


清单 22. 查找 bindingTemplate 
                    <find_binding generic="2.0" xmlns="urn:uddi-org:api_v2" >   <tModelBag>      <tModelKey>66999A50-D5F4-11DA-9A50-FA44D6AD622A</tModelKey>   </tModelBag></find_binding>

此查询将返回一个 serviceList。请参见清单 23


清单 23. 返回的服务
                    <serviceList generic="2.0" operator="uddi.sourceOperator"                      xmlns="urn:uddi-org:api_v2">   <serviceInfos>      <serviceInfo             serviceKey="064B4170-D5F5-11DA-8170-A74C17FA61A7"             businessKey="1A3DB880-D5F4-11DA-B880-F94D3591C691">          <name>ClassifiedService</name>      </serviceInfo>   </serviceInfos></serviceList>

 find_business 的情况(必须随后再次进行相关操作,请求有关 businessEntity 的信息)一样,将随后基于 serviceKey 具体地请求有关服务器的信息。请参见清单 24


清单 24. 获得服务详细信息
                    <get_serviceDetail generic="2.0" xmlns="urn:uddi-org:api_v2" >   <serviceKey>064B4170-D5F5-11DA-8170-A74C17FA61A7</serviceKey></get_serviceDetail>

此查询将为每个指定的 serviceKey 返回一个 serviceDetail。请参见清单 25


清单 25. 返回的服务详细信息
                    <serviceDetail generic="2.0" operator="uddi.sourceOperator"     xmlns="urn:uddi-org:api_v2">   <businessService           serviceKey="064B4170-D5F5-11DA-8170-A74C17FA61A7"          businessKey="1A3DB880-D5F4-11DA-B880-F94D3591C691">      <name>ClassifiedService</name>      <bindingTemplates>         <bindingTemplate              bindingKey="904BD800-D53A-11DA-B055-850A1DA99D79">            <accessPoint>      http://www.daily-moon.com:8080/axis2/services/ClassifiedService            </accessPoint>...         <keyedReference               tModelKey="C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"               keyName="ntis-gov:naics:1997" keyValue="511110" />      </categoryBag>   </businessService>   <businessService>...   </businessService></serviceDetail>

Gene 现在已经明确了自己要进行的工作,可以开始进行编码了。

以编程方式使用 UDDI

Gene 最后的工作是建立一组概念验证应用程序,以说明其他团队可以如何基于 UDDI 注册中心构建应用程序。

设置

使用 UDDI 注册中心的第一步是注册软件本身,但 Gene 惊喜地发现,由于使用 Apache Geronimo 作为其 Web 应用服务器,该步骤已经完成;Geronimo 随机安装了 Apache jUDDI。

这个工作已经完成,Gene 接下来必须决定将实际如何访问注册中心了。此处,他有两个选择。第一个选择是使用基于 UDDI 的 API(如 jUDDI 或 UDDI4J 中包含的 API)直接访问。第二个选择是使用 Java API for XML Registries (JAXR) 实现。JAXR 旨在用于访问任何注册中心,其主要目标是提供 UDDI 和 ebXML 支持、

Gene 决定直接从源代码着手,并下载 UDDI4J。而 UDDI4J 依赖于随 Apache Axis 分发的一些库,因此他也将其下载了。(请注意,尽管本项目的前面部分使用了 Axis2,但 UDDI4J 依赖于随 Axis 1 分发的库。)

安装过程相当简单,仅需要确保所有必要的 UDDI4J 和 Axis *.jar 文件位于 CLASSPATH 上即可。请参见清单 26


清单 26. 设置 CLASSPATH
                     CLASSPATH=.;<UDDI4J_HOME>\lib\uddi4j.jar;<AXIS_HOME>\lib\axis.jar;<AXIS_HOME>\lib\saaj.jar;<AXIS_HOME>\lib\jaxrpc.jar;<AXIS_HOME>\lib\log4j-1.2.8.jar;<AXIS_HOME>\lib\commons-logging-1.0.4.jar;<AXIS_HOME>\lib\commons-discovery-0.2.jar;

最后,Gene 需要一个用户名和密码,以连接到注册中心。就这方面而言,每个注册中心都彼此不同,因此我们不打算详细讨论细节。Apache Geronimo 中的 UDDI 缺省安装使用的用户名和密码均为 "juddi"

回页首

连接到注册中心

任何应用程序中的第一步都是连接到注册中心。请参见清单 27


清单 27. 连接到注册中心
                    import org.uddi4j.client.UDDIProxy;import org.uddi4j.response.AuthToken;import org.uddi4j.transport.TransportFactory;public class CreateNewBusiness {   public static void main (String args[]){      String inquiryURL = "http://localhost:8080/juddi/inquiry";      String publishURL = "http://localhost:8080/juddi/publish";      String userId = "juddi";      String credential = "juddi";      String transportClass =              "org.uddi4j.transport.ApacheAxisTransport";      System.setProperty(TransportFactory.PROPERTY_NAME,                          transportClass);      UDDIProxy proxy = new UDDIProxy();      try {         proxy.setInquiryURL(inquiryURL);         proxy.setPublishURL(publishURL);         AuthToken token = proxy.get_authToken(userId, credential);         System.out.println("Security authToken:" +                                 token.getAuthInfoString());      } catch(Exception e ) {         e.printStackTrace();               }   }}

从代码的起始处开始,Gene 设置了将在整个应用程序中使用的各个值。具体来说,Gene 设置了 TransportFactory 的类名称。UDDI4J 可以使用任意数量的相关类,如在此处看到的随 Apache Axis 提供的类、随旧 Apache SOAP 包提供的类或任何类似的类。

最后,他设置了注册中心的 inquiryURL  publishURL 值(通常二者将不同,这其中的部分原因是因为发布通常是使用 SSL 完成的)及请求和授权标记。

此标记很重要,因为每个请求都用其来指示自己已经过服务器的身份验证。由于每个注册中心的处理方式不同,因此随每个请求发送用户名和密码并不实际(也不安全)。

Gene 运行应用程序时,看到了以下的结果:
Security authToken:authToken:505A7DE0-D897-11DA-A5BF-ADFF17B378CF

回页首

创建业务实体

接下来 Gene 要创建业务实体。请参见清单 28


清单 28. 创建 businessEntity 
                    import org.uddi4j.client.UDDIProxy;import org.uddi4j.response.AuthToken;import org.uddi4j.transport.TransportFactory;import org.uddi4j.datatype.business.BusinessEntity;import org.uddi4j.response.BusinessDetail;import org.uddi4j.datatype.business.Contact;import org.uddi4j.datatype.business.Contacts;import org.uddi4j.util.KeyedReference;import org.uddi4j.util.IdentifierBag;import org.uddi4j.util.CategoryBag;import org.uddi4j.datatype.tmodel.TModel;import java.util.Vector;public class CreateNewBusiness {   public static void main (String args[]){      String inquiryURL = "http://localhost:8080/juddi/inquiry";      String publishURL = "http://localhost:8080/juddi/publish";      String userId = "juddi";      String credential = "juddi";      String businessName = "The Daily Moon";      String alt_businessName = "Daily Moon";      String contact_personName = "Pat Moonie";      String contact_phone = "212-555-1212";      String identifier_homepage = "http://www.daily-moon.com";      String category_NAICS = "511110";      String transportClass =                "org.uddi4j.transport.ApacheAxisTransport";      System.setProperty(TransportFactory.PROPERTY_NAME,                          transportClass);      UDDIProxy proxy = new UDDIProxy();      try {         proxy.setInquiryURL(inquiryURL);         proxy.setPublishURL(publishURL);         AuthToken token = proxy.get_authToken(userId, credential);         System.out.println("Security authToken:" +                                 token.getAuthInfoString());         Vector entities = new Vector();         BusinessEntity newBusiness =                         new BusinessEntity("", businessName);         Contact newContact = new Contact(contact_personName);         Vector contactsVector = new Vector();         contactsVector.addElement(newContact);         Contacts contacts = new Contacts();          contacts.setContactVector(contactsVector);         newBusiness.setContacts(contacts);            KeyedReference taxid =                new KeyedReference(TModel.HOMEPAGE_TMODEL_KEY,                                      identifier_homepage);         IdentifierBag idBag = new IdentifierBag();         idBag.add(taxid);         newBusiness.setIdentifierBag(idBag);         KeyedReference category =                new KeyedReference(TModel.NAICS_TMODEL_KEY,                                   category_NAICS);         CategoryBag catBag = new CategoryBag();         catBag.add(category);         entities.addElement(newBusiness);         BusinessDetail bd =                      proxy.save_business(token.getAuthInfoString(),                                          entities);         Vector businessEntities = bd.getBusinessEntityVector();         BusinessEntity returnedBusinessEntity =                        (BusinessEntity)(businessEntities.elementAt(0));         System.out.println("The new businessKey:"                   + returnedBusinessEntity.getBusinessKey());      } catch(Exception e ) {         e.printStackTrace();               }   }}

创建 businessEntity 的过程所涉及的主要步骤是按照其在 XML 中的形式构建各个元素,而将“复数”(如 "contacts""tModelInfos"等)作为矢量处理。

在本例中,Gene 构建了 contact 矢量、identifierBag  categoryBag,并将其添加到 BusinessEntity 对象中。对象本身会被添加到矢量中,此矢量中包含 save_business() 方法和授权标记。

所得到的结果是一个 BusinessDetail 对象,其中包含一个或多个 BusinessEntity 对象,与数据库中的情况一样。在本例中,Gene 在没有为 businessKey 指定值的情况下提交了 BusinessEntity,从而让注册中心知道要创建一个新对象和一个相应的新键。通过从矢量中检索第一个 BusinessEntity,Gene 可以随后获得新 businessKey

运行应用程序时,他获得了清单 29 中所示的结果:


清单 29. 获得新 businessKey
                    Security authToken:authToken:1D05DB90-D899-11DA-A5BF-D3513DE466CAThe new businessKey:1D359E20-D899-11DA-A5BF-F9EAF6D4E3F7

回页首

创建服务接口 tModel

有了相关的业务信息后,Gene 接下来开始添加服务接口。请参见清单 30


清单 30. 创建 tModel 
                    import org.uddi4j.client.UDDIProxy;import org.uddi4j.response.AuthToken;import org.uddi4j.transport.TransportFactory;import java.util.Vector;import org.uddi4j.datatype.tmodel.TModel;import org.uddi4j.response.TModelDetail;import org.uddi4j.datatype.OverviewURL;import org.uddi4j.datatype.OverviewDoc;import org.uddi4j.util.CategoryBag;import org.uddi4j.util.KeyedReference;public class CreateNewInterface{   public static void main (String args[]){      String inquiryURL = "http://localhost:8080/juddi/inquiry";      String publishURL = "http://localhost:8080/juddi/publish";      String userId = "juddi";      String credential = "juddi";      String transportClass =             "org.uddi4j.transport.ApacheAxisTransport";      System.setProperty(TransportFactory.PROPERTY_NAME,             transportClass);      UDDIProxy proxy = new UDDIProxy();      try {         proxy.setInquiryURL(inquiryURL);         proxy.setPublishURL(publishURL);         AuthToken token = proxy.get_authToken(userId, credential);         System.out.println("Security authToken:" +                                    token.getAuthInfoString());         Vector tModels = new Vector();         TModel tModel = new TModel("",              "http://www.daily-moon.com/classifieds-interface");         tModel.setDefaultDescriptionString(               "Interface for the Daily Moon Classified "+               "Department web application");         OverviewDoc overviewDoc = new OverviewDoc();         overviewDoc.setDefaultDescriptionString(                                   "WSDL interface document");         OverviewURL overviewURL = new OverviewURL( "http://www.nicholaschase.com/ClassifiedsService-interface.wsdl");         overviewDoc.setOverviewURL(overviewURL);         tModel.setOverviewDoc(overviewDoc);         KeyedReference wsdlNotation =             new KeyedReference(TModel.TYPES_TMODEL_KEY,                                "wsdlSpec",                  "C1ACF26D-9672-4404-9D70-39B756E62AB4");         KeyedReference typeNotation =             new KeyedReference("ntis-gov:naics:1997",                                "511110",                  "C0B9FE13-179F-413D-8A5B-5004DB8E5BB2");         CategoryBag catBag = new CategoryBag();         catBag.add(wsdlNotation);         catBag.add(typeNotation);         tModel.setCategoryBag(catBag);         tModels.add(tModel);         TModelDetail tModelDetail =                          proxy.save_tModel(token.getAuthInfoString(), tModels);         Vector tModelVector = tModelDetail.getTModelVector();         TModel tModelReturned = (TModel)(tModelVector.elementAt(0));         System.out.println("TModel Saved: " +                                 tModelReturned.getNameString());         System.out.println("TModel Key  : " +                                 tModelReturned.getTModelKey());      } catch(Exception e ) {         e.printStackTrace();               }   }}

和前面一样,Gene 着手使用 Java 代码建模 XML 元素。他首先创建 TModel,同样没有提供键,以指示他正在创建新项目,并设置其描述。他然后创建 OverviewDoc,并完成描述和 OverviewURL。接下来,他为 CategoryBag 创建 KeyedReferences。请注意,他引用的是特定的 tModels,因此为每个项提供了 tModelKey;此值是可选的。最后,他保存 TModel(一旦再次从返回的值检索到该对象)。

运行了应用程序后,Gene 得到了清单 31 中所示的结果:


清单 31. 获得新 tModelkey
                    Security authToken:authToken:3312DF20-D8A1-11DA-A5BF-D754085751C9TModel Saved: http://www.daily-moon.com/classifieds-interfaceTModel Key  : uuid:332F67D0-D8A1-11DA-A5BF-DF15F533BFA1

回页首

创建服务

创建服务本身的任务遵循相同的基本过程。请参见清单 32


清单 32. 创建服务
                    import org.uddi4j.client.UDDIProxy;import org.uddi4j.response.AuthToken;import org.uddi4j.transport.TransportFactory;import java.util.Vector;import org.uddi4j.datatype.business.BusinessEntity;import org.uddi4j.datatype.service.BusinessService;import org.uddi4j.datatype.binding.BindingTemplate;import org.uddi4j.datatype.binding.BindingTemplates;import org.uddi4j.util.CategoryBag;import org.uddi4j.util.KeyedReference;import org.uddi4j.datatype.binding.AccessPoint;import org.uddi4j.datatype.binding.TModelInstanceDetails;import org.uddi4j.datatype.binding.TModelInstanceInfo;import org.uddi4j.datatype.binding.InstanceDetails;import org.uddi4j.datatype.OverviewDoc;import org.uddi4j.datatype.OverviewURL;import org.uddi4j.datatype.tmodel.TModel;import org.uddi4j.response.ServiceDetail;public class CreateNewService{   public static void main (String args[]){      String inquiryURL = "http://localhost:8080/juddi/inquiry";      String publishURL = "http://localhost:8080/juddi/publish";      String userId = "juddi";      String credential = "juddi";      String businessKey = "1D359E20-D899-11DA-A5BF-F9EAF6D4E3F7";      String transportClass =            "org.uddi4j.transport.ApacheAxisTransport";      System.setProperty(TransportFactory.PROPERTY_NAME,                          transportClass);      UDDIProxy proxy = new UDDIProxy();      try {         proxy.setInquiryURL(inquiryURL);         proxy.setPublishURL(publishURL);         AuthToken token = proxy.get_authToken(userId, credential);         System.out.println("Security authToken:" +                                    token.getAuthInfoString());         BusinessService businessService = new BusinessService("");         businessService.setDefaultNameString(                                   "ClassifiedService",null);         businessService.setBusinessKey(businessKey);         BindingTemplate bindingTemplate = new BindingTemplate();         AccessPoint accessPoint = new AccessPoint("http://www.daily-moon.com:8080/axis2/services/ClassifiedService",                                                    "http");         bindingTemplate.setAccessPoint(accessPoint);         TModelInstanceDetails tModelDetails =                                  new TModelInstanceDetails();         TModelInstanceInfo tModelInstanceInfo =                 new TModelInstanceInfo(                       "332F67D0-D8A1-11DA-A5BF-DF15F533BFA1");         InstanceDetails instanceDetails = new InstanceDetails();         OverviewDoc overviewDoc = new OverviewDoc();         OverviewURL overviewURL = new OverviewURL("http://www.nicholaschase.com/ClassifiedService-impl.wsdl");         overviewDoc.setOverviewURL(overviewURL);         instanceDetails.setOverviewDoc(overviewDoc);         tModelInstanceInfo.setInstanceDetails(instanceDetails);                  tModelDetails.add(tModelInstanceInfo);         bindingTemplate.setTModelInstanceDetails(tModelDetails);         BindingTemplates bindingTemplates = new BindingTemplates();         bindingTemplates.add(bindingTemplate);         businessService.setBindingTemplates(bindingTemplates);         KeyedReference wsdlNotation =             new KeyedReference(TModel.TYPES_TMODEL_KEY,                                "wsdlSpec",                  "C1ACF26D-9672-4404-9D70-39B756E62AB4");         KeyedReference typeNotation =             new KeyedReference("ntis-gov:naics:1997",                                "511110",                  "C0B9FE13-179F-413D-8A5B-5004DB8E5BB2");         CategoryBag catBag = new CategoryBag();         catBag.add(wsdlNotation);         catBag.add(typeNotation);         businessService.setCategoryBag(catBag);         Vector services = new Vector();         services.addElement(businessService);                  ServiceDetail serviceDetail = proxy.save_service(                        token.getAuthInfoString(),services);         Vector businessServices =                  serviceDetail.getBusinessServiceVector();         BusinessService businessServiceReturned =                 (BusinessService)(businessServices.elementAt(0));         String serviceKey = businessServiceReturned.getServiceKey();         System.out.println("The Name: "+                          businessServiceReturned.getDefaultNameString());         System.out.println("The ServiceKey: "+ serviceKey);      } catch(Exception e ) {         e.printStackTrace();               }   }}

现在,Gene 发现此模式非常熟悉;按照其在 XML 文档中的形式创建这些对象。创建 BusinessService,添加 businessKey。创建BindingTemplate,同时确保引用创建接口 tModel 时创建的 tModelKey 和实际 WSDL 文件的 URL。创建 categoryBag,添加它,并保存服务。检索服务及其名称和键。运行应用程序后,Gene 获得了清单 33 中所示的结果。


清单 33. 检索服务名称键
                    Security authToken:authToken:E4203390-D8A5-11DA-A5BF-8F6C6FEDFEBDThe Name: ClassifiedServiceThe ServiceKey: E43F2D40-D8A5-11DA-A5BF-880A3C83523B

回页首

查找公司

接下来,Gene 开始着手将这些搜索投入实际使用。他首先进行公司搜索,尝试确保报社的信息已被输入注册中心中,且并未重复。请参见清单 34


清单 34. 查找公司
                    import org.uddi4j.client.UDDIProxy;import org.uddi4j.response.AuthToken;import org.uddi4j.transport.TransportFactory;import java.util.Vector;import org.uddi4j.datatype.Name;import org.uddi4j.response.BusinessInfo;import org.uddi4j.response.BusinessList;import org.uddi4j.util.FindQualifier;import org.uddi4j.util.FindQualifiers;public class FindBusiness{   public static void main (String args[]){      String inquiryURL = "http://localhost:8080/juddi/inquiry";      String publishURL = "http://localhost:8080/juddi/publish";      String userId = "juddi";      String credential = "juddi";      String transportClass =              "org.uddi4j.transport.ApacheAxisTransport";      System.setProperty(TransportFactory.PROPERTY_NAME,                          transportClass);      UDDIProxy proxy = new UDDIProxy();      try {         proxy.setInquiryURL(inquiryURL);         proxy.setPublishURL(publishURL);         AuthToken token = proxy.get_authToken(userId, credential);         System.out.println("Security authToken:" +                                     token.getAuthInfoString());         String businessToFind = "The Daily Moon";         Vector names = new Vector();         names.add(new Name(businessToFind));         FindQualifiers findQualifiers = new FindQualifiers();         Vector qualifier = new Vector();         qualifier.add(new FindQualifier("exactNameMatch"));         findQualifiers.setFindQualifierVector(qualifier);         BusinessList businessList =                         proxy.find_business(names, null, null,                                    null,null,findQualifiers,5);         Vector businessInfoVector  =              businessList.getBusinessInfos().getBusinessInfoVector();         for( int i = 0; i < businessInfoVector.size(); i++ ){            BusinessInfo businessInfo =                       (BusinessInfo)businessInfoVector.elementAt(i);            System.out.println(businessInfo.getDefaultNameString() +                               ": " + businessInfo.getBusinessKey());         }      } catch(Exception e ) {         e.printStackTrace();               }   }}

Gene 正在查找特定的 name 值,因此这就是他所构建的搜索类型。他创建了具有单个项的 Name 对象的 Vector,并创建了FindQualifier 来指定要查找精确匹配。最后,他将请求提交给 find_business() 方法,此方法接受以下参数:任何discoveryURLs、一个 IdentifierBag、一个 CategoryBag、一个 TModelBag、任何 FindQualifiers 以及要返回的最大结果数。

所得到的结果是一个 BusinessList,Gene 将对其进行遍历,以查看所有的值,从而确定信息是否重复。对于每个值,他将打印出名称和 businessKey,如清单 35 中所示。


清单 35. 公司名称和 businessKey
                    Security authToken:authToken:DBEAE280-D8A8-11DA-A5BF-A6D55D3D237DThe Daily Moon: 1D359E20-D899-11DA-A5BF-F9EAF6D4E3F7The Daily Moon: 1A3DB880-D5F4-11DA-B880-F94D3591C691

在本例中,他可以看到信息出现了重复,因此可以将多余的 businessKey 提交给 UDDIProxy 对象的 delete_business() 方法。

回页首

查找服务

接下来,Gene 希望确保能够基于类别查找 ClassifiedService。请参见清单 36


清单 36. 查找某种类型的服务
                    import org.uddi4j.client.UDDIProxy;import org.uddi4j.response.AuthToken;import org.uddi4j.transport.TransportFactory;import java.util.Vector;import org.uddi4j.util.CategoryBag;import org.uddi4j.util.KeyedReference;import org.uddi4j.datatype.tmodel.TModel;import org.uddi4j.datatype.binding.BindingTemplate;import org.uddi4j.datatype.binding.TModelInstanceInfo;import org.uddi4j.datatype.binding.TModelInstanceDetails;import org.uddi4j.datatype.binding.AccessPoint;import org.uddi4j.datatype.binding.InstanceDetails;import org.uddi4j.datatype.service.BusinessService;import org.uddi4j.response.ServiceDetail;import org.uddi4j.response.ServiceInfo;import org.uddi4j.response.ServiceList;public class FindTypeOfService{   public static void main (String args[]){      String inquiryURL = "http://localhost:8080/juddi/inquiry";      String publishURL = "http://localhost:8080/juddi/publish";      String userId = "juddi";      String credential = "juddi";      String transportClass =                 "org.uddi4j.transport.ApacheAxisTransport";      System.setProperty(TransportFactory.PROPERTY_NAME,                          transportClass);      UDDIProxy proxy = new UDDIProxy();      try {         proxy.setInquiryURL(inquiryURL);         proxy.setPublishURL(publishURL);         AuthToken token = proxy.get_authToken(userId, credential);         System.out.println("Security authToken:" +                                  token.getAuthInfoString());         KeyedReference wsdlNotation =             new KeyedReference(TModel.TYPES_TMODEL_KEY,                                "wsdlSpec",                  "C1ACF26D-9672-4404-9D70-39B756E62AB4");         KeyedReference typeNotation =             new KeyedReference("ntis-gov:naics:1997",                                "511110",                  "C0B9FE13-179F-413D-8A5B-5004DB8E5BB2");         CategoryBag catBag = new CategoryBag();         catBag.add(wsdlNotation);         catBag.add(typeNotation);         ServiceList serviceList = proxy.find_service(null, null,                                              catBag,null,null,5);         Vector serviceInfoVector =                serviceList.getServiceInfos().getServiceInfoVector();         for( int i = 0; i < serviceInfoVector.size(); i++ ){            ServiceInfo serviceInfo =                    (ServiceInfo)serviceInfoVector.elementAt(i);            System.out.println("Service name: " +                           serviceInfo.getDefaultNameString());            System.out.println("Service key: " +                           serviceInfo.getServiceKey());            ServiceDetail serviceDetail =                 proxy.get_serviceDetail(serviceInfo.getServiceKey());            BusinessService thisService =                                 (BusinessService)serviceDetail                           .getBusinessServiceVector().elementAt(0);            if (thisService.getBindingTemplates().size() > 0){               BindingTemplate thisBinding =                                      (BindingTemplate)thisService                                 .getBindingTemplates().get(0);               TModelInstanceDetails tModelDetails =                               thisBinding.getTModelInstanceDetails();               TModelInstanceInfo tModelInfo = tModelDetails.get(0);               InstanceDetails instanceDetails =                               tModelInfo.getInstanceDetails();               String wsdlDocument =                               instanceDetails.getOverviewDoc()                                             .getOverviewURLString();               System.out.println("Document located at " +                                              wsdlDocument);            }         }      } catch(Exception e ) {         e.printStackTrace();               }   }}

Gene 首先创建 CategoryBag,以便搜索与报纸有关的所有 WSDL 指定的服务。所得到的结果是一个 ServiceList,他可通过对其进行遍历来获得更多的信息。ServiceInfo 对象表示结果中的服务,他将对每个此类对象进行遍历,并同时显示其名称和 serviceKey

不过,如果 Gene 希望得到更多的详细信息,如 WSDL 文件的 URL,他必须使用 serviceKey 来请求服务的ServiceDetailServiceDetail 具有零个或零个以上与其关联的 BindingTemplates,如果找到一个,则将通过TModelInstanceDetails 对象向下获得 InstanceDetails 对象,此对象中包含 OverviewDoc,也能据此得到 OverviewURL。结果将不仅显示所查询服务的名称和 serviceKey,还会显示供用户查找更多信息的 URL。请参见清单 37


清单 37. 最终结果
                    Security authToken:authToken:59FE37C0-D8AF-11DA-A5BF-A79333DB92F9Service name: ClassifiedServiceService key: E43F2D40-D8A5-11DA-A5BF-880A3C83523BDocument located at          http://www.nicholaschase.com/ClassifiedService-impl.wsdl

结束语

分布式环境要求使用集成存储库,以便用户和开发人员了解哪些功能可用以及能在何处找到这些功能,Web 服务也不例外。本教程说明了统一描述、发现和集成 (UDDI) 的使用,UDDI 提供了保存有关可用企业和服务的信息的注册中心。

在本教程中,您了解了以下内容:

  • 什么是 UDDI 以及它为何重要
  • 如何设计 UDDI 数据的结构
  • 如何区分 SOAP 接口和实现
  • 如何在 UDDI 注册中心中查找数据
  • 如何使用 Java 与 UDDI 注册中心交互

本系列的后续部分将介绍如何应对不断增加的安全需求,并对 WS-Security 和 WS-Policy 进行探讨。

-----------------

 
-
-
-
-
--
-
-
--
-
-