内容


使用 HTTP/POST 进行 Internet/分布式计算

在同一 Internet 协议下建立语义 Web 和 Web 服务之间的联系

Comments

引言

Web 服务提供程序和服务请求程序之间的基本关系是请求和响应的关系。服务请求程序通常会将请求发送到服务提供程序,服务提供程序会提供请求的解决方案(如果无法对请求进行处理,则为错误消息)来进行响应。在特定的应用程序涉及多个 Web 服务时,则必须考虑进行服务聚合或集成。此时有望可以按照完成特定任务所需的特定顺序对 Web 服务进行自动地动态发现、组合和调用。语义 Web 服务的研究和开发的症结在于服务聚合流程的建模和描述。

变得越来越复杂的语义 Web 服务框架表明 Web 服务技术方面有很大的局限。与万维网的广受欢迎(任何可以连接到 Internet 的人都可以使用万维网)相比,Web 服务当前只是一个专门为计算机专业人士提供的工具,而不是面向各个领域的用户。Mazzocchi 认为,“即使是 XML 人员也并不理解 RDF/OWL 中的语义 Web 语言”(有关更多信息,请参阅参考资料)。不过,服务请求程序并不需要了解服务提供程序如何处理其请求,而且请求程序也没有责任了解如何通过逻辑建模提供服务。语义 Web 服务方面的很多研究并不关注对服务的意义进行描述,而是将重点放在服务聚合的过程,后者对服务请求程序并没有意义。

本文首先将回顾语义 Web 服务技术的最新发展。接着将讨论如何将组合 Web 服务转换为原子过程——以及如何描述单个请求-响应关系中在不同的服务领域中实际集成多个 Web 服务的此类原子 Web 服务的服务语义。然后,本文将给出一些建议方法,以演示可以如何使用 HTTP/POST 实现 Internet/分布式计算或语义/智能 Web 服务,并讨论未来开发工作中的挑战。

SOAP Web 服务

Web 服务可为用不同的平台和编程语言开发的应用程序提供新层次的互操作性。每个服务都对具体的业务功能进行封装,可以使用 SOAP 绑定 HTTP 的方式,通过 Web 服务描述语言(Web Services Description Language,WSDL)接口调用此类业务功能,以在服务请求程序和服务提供程序之间传递消息。SOAP 使用序列化器和反序列化器将软件应用程序的本机语言转换为 SOAP 协议,以在网络上传输请求和响应。Deem (2002) 指出,“SOAP 提供的最有用的功能是可以在平台特定的格式和用于 SOAP 消息的 XML 格式之间进行数据转换。正是得益于这个功能,运行于一个平台上的功能才能将信息传递给运行于另一个平台上的应用程序。”有关更多信息,请参阅参考资料

SOAP 可以解决语法级别的互操作性问题,但语义问题仍然未得到解决。Web 服务将对具体业务功能进行封装,这就隐藏了供请求程序用于理解数据和服务语义所需的信息。用户必须通过对象和数据流关系图才能了解实现细节,以便部署服务(Shi,2004)。在实际情况中,WSDL 接口仅反映对象、数据类型、函数和变量的命名系统和层次关系(编程人员开发 Web 服务时会使用这些信息),而不会反映数据和服务的含义(语义)。Web 服务的语法体系结构无法清楚地表达数据和服务的含义;不过其中却充满了冗余信息。

图 1(Shi,2004)阐释了这些问题。此关系图显示了如何理解和调用 ESRI 提供的 Address Finder Web 服务的 findAddress 函数,ESRI 是一家全球领先的地理信息系统 (GIS) 软件供应商。我们可以在以下位置找到 Address Finder Web 服务的 WSDL 文件:http://arcweb.esri.com/services/v2/AddressFinder.wsdl

可以使用 findAddress 函数来查找给定街道地址的位置(经度和纬度)。根据此付费 Web 服务的语义,服务请求程序必须:

  1. 提供有效的用户名、密码,以调用身份验证 Web 服务来获得身份验证令牌
  2. 指定要在此应用程序中使用的数据源
  3. 使用此动态生成的令牌和指定的数据源并提供地址来调用 findAddress 函数。

最后此 Web 服务将返回输入地址的经度和纬度。

简要总结如下:

  • 输入要求 为用户名、密码、数据源、街道地址、城市、州、邮政编码
  • 输出 将为与输入地址匹配的经度和纬度的列表,或者一条错误消息。

不过,我们无法从图 1 或此 WSDL 文件中找到如此直接的信息。而且,服务请求程序无法根据关系图确定必须使用两个 Web 服务来获得期望的结果。其中一些字符串数据类型显得很有意义,如:street、intersection、city、state、province、zone 和 country。不过,WSDL 中充满了冗余信息,这些信息对新手或外行而言并不是必需的。对于 WSDL 的大部分内容,我们都必须对其含义进行猜测。例如,LocationInfo、ArrayOfLocation、Location[]、description1、description2、addressFinderOptions、token、matchType 都是各种对象、数据类型、函数、变量的名称,但却并不能表示其真正的含义。谁会知道 WSDL 中 description1、description2、addressFinderOptions、token、matchType 等等的含义呢?总之,WSDL 中包含的名称并不表示各个 Web 服务的含义,各个 Web 服务的语义并不包含在此 WSDL 文件中。

图 1. 用于帮助理解和调用 Address Finder Web 服务的工作流程图
工作流程图
工作流程图

语义 Web 服务

按照 W3C 的规定,WSDL 将消息格式、数据类型、传输协议和传输序列化格式的服务描述定义为 Web 服务接口的可由计算机处理的规范。Web 服务的语义是关于服务行为的共同期望,是服务请求程序和服务提供程序之间有关交互的目的和结果的“协定”。“服务描述表示控制与特定服务交互的机制的协定,而语义则表示控制交互的目的和结果的协定”(有关更多信息,请参阅参考资料)。它被定义为“服务描述是服务的可由计算机处理的描述”,其中“包含服务所交互的消息的可由计算机处理的描述”,而且“可以包含服务语义的描述”;另外,“服务语义是关于组成服务的服务任务的”,“应在服务描述中进行标识”,而且“可以使用形式化的可由计算机处理的语言进行描述”(有关更多信息,请参阅参考资料)。

服务请求程序和服务提供程序之间的通信建立了 Web 服务中的基本关系:请求和响应。服务的语义描述的用途在于,可帮助服务请求程序在调用服务前理解服务的内容、范围、要求、输出和效果。服务描述充当服务发现和匹配的广告,以帮助请求程序知道正确的服务,并进一步支持实现自动化或半自动化服务组合和调用。由于这个原因,所谓的“共同期望”应面向请求程序,即,服务提供程序的广告应以请求程序的需求为核心,从而提供一个解决方案来帮助服务请求程序。考虑以下这个常见而又实际的案例,假如客户为到纽约旅行一段时间而使用 www.expedia.com 购买机票、预订出租车和酒店,www.expedia.com 将在服务器端进行搜索,然后向客户返回所有可能的解决方案以便进行请求,这是一个关于可提供机票、汽车出租公司和酒店的相关信息的组合,如价格、时间表等。因此,可以得出结论,服务请求程序所期望的是发出请求并从服务提供程序获得答案,而不是获知有关服务提供程序如何处理请求的框架和逻辑过程。

服务聚合是一项重要的功能。当前,语义 Web 服务有很多建议方法,例如:Ontology Web Language (OWL)-based Web service ontology (OWL-S)、(W3C, 2004a)、Web Service Modeling Ontology (WSMO)(W3C,2005)和 Web Service Semantics (WSDL-S) (Akkiraju 等,2005)。OWL-S 和 WSMO 与语义 Web 技术有很深的渊源,使用了不同的逻辑对服务流程进行建模(对于 OWL-S,为 Description Logic;而对于 WSMO,则为 Frame Logic)。此类方法将构建复杂的框架,以将不同的 Web 服务链接为组合过程,并通过使用逻辑语句与未知的服务、函数、输入变量和输出结果关联,从而描述组合这些过程的方式。例如,为了进行服务聚合,OWL-S 使用流程模型,而 WSMO 使用各种中介。

在考虑了面向请求程序的共同期望后,OWL-S、WSMO 和其他方法将重点放在服务聚合的流程模型,而不是向请求提供答案。服务聚合器提供的是要求进行解释的复杂框架。如果从 OWL-S 和 WSMO 中去掉这些流程和模型,剩下的将只是服务描述(OWL-S 的 Profile 或 WSMO 的 Goals)和 Web 服务本身。总的说来,服务聚合流程并不提供服务的语义含义,且此类聚合不应向等待其请求的应答(而不是框架)的服务请求程序公开。

对处理语义 Web 服务而言,WSDL-S 也是一种有问题的方法。由于 WSDL 是面向对象编程 (OOP) 的产物,因此 WSDL 文档只能反映 OOP 的内容,而不能表述数据和服务的含义。如图 1 中所示,由于 WSDL 文档包含许多在编程中使用的中间对象和数据,而这些内容对数据和服务没有直接意义,因此许多 WSDL 文档是多余的,与服务请求程序并不相关(有关更多信息,请参阅参考资料)。

图 1 所示的示例中,ESRI 的 ArcWeb 服务提供 Address Finder Web 服务。请求程序将地址信息发送到服务提供程序,而后者将随后向请求程序返回位置信息(经度和纬度)。从 OOP 的角度而言,服务提供程序会将 LocationInfo 对象返回给请求程序。由于在输入地址不唯一且描述不完全的情况下,可能会标识多个与输入地址匹配的位置,因此此时 LocationInfo 对象中会包含一个 Location 对象数组。对于每个 Location 对象,除了其中的多个属性外,Location 对象还包含 Point 对象,而后者最终包含了经度和纬度的值。

当请求程序将请求发送到服务器时,预期的应答是与输入地址(而不是编程过程中使用的其他对象、数据类型、属性等)匹配的所有可能位置信息(经度和纬度)。由于 Web 服务的语义并不包含在 WSDL 文件中,因此 WSDL-S 并不是用于描述服务语义的正确方法,相反,它描述的是编程过程中的细节而不是服务的含义。由于并非 WSDL 中的所有内容都有意义,因此将语义添加到 WSDL 无疑是一大挑战,其原因在于很难描述 OOP 中所使用的中间对象。尽管服务提供程序可以向 WSDL 文件中的所有元素添加语义,但这样做并没有意义,因为和图 1 中所示的一样,WSDL 文件中的许多元素与请求并不相关。特别在服务基于另一个服务的结果时,将语义添加到各个服务并不能帮助请求程序理解不同的服务和提供程序中的内部关系。不过,如果只有文档中的小部分得到描述,有选择地将语义添加到 WSDL 中会使文档难于解释,从而最终增加请求程序理解数据和服务的含义的难度。

重新构建语义 Web 服务体系结构

语义 Web 服务开发应将重点放在通信过程中的基本请求-响应关系上。语义请求和响应(Semantic Request and Response,SRR)方法(有关更多信息,请参阅参考资料)可以将所有组合 Web 服务转换为原子服务,以在不使用这些逻辑框架的情况下提供服务聚合。SRR 将重点放在服务请求程序的期望上,即发出请求,然后从服务提供程序获得答案。服务请求程序不应关心服务提供程序如何处理请求,而应仅要求从服务提供程序获得答案。这样,语义 Web 服务研究和开发工作可以将重点放在描述请求和响应的服务语义上,而忽略过程(这对请求程序没有意义)。

Web 服务聚合可以分为三类:

  1. 并行流程,每个 Web 服务彼此独立
  2. 顺序流程,一个服务可能依赖于其他 Web 服务的结果
  3. 混合流程,包含前面两种类型

SRR 可以将此类组合 Web 服务转换为原子服务,如图 2 中所示。这种方法要求服务提供程序在向请求程序提供集成的服务前处理所有聚合流程,这样,请求就无需了解提供程序如何处理请求了。

图 2. Web 服务聚合中的组合流程的分类
分类
分类

对于并行服务流程(如机票、酒店和出租车预订服务的服务提供程序链)而言,这些服务彼此独立,而这些逻辑模型将尝试确定不同服务、函数和变量间的关系。不过,SRR 方法为服务请求程序提供了直接的服务描述。在图 2 中,函数 F1 标识输入变量,而对象 x1 中包含两个属性 a1 和 a2。编程人员知道如何为不同的方法创建函数,而 SRR 将生成请求 XML 文档,以指定输入要求为 (a1, a2, y1, x2, x3, y3),而响应 XML 文档包含的输出为 (z1, z2, z3)。

表 1 的顺序流程示例中,请求 XML 文档包含输入变量 (x1, y1, x2),而响应 XML 文档中可以包含对象 z2 的所有可能属性,即,数值是由对象 z2 组成的数组,因此这些 XML 文档的内容如下所示:

表 1. 顺序流程
服务请求服务响应
<Service Request>
...
<x1>value 1</x1>
<y1>value 2</y1>
<x2>value 3</x2>
...

</Service Request>
<Service Response>
...
<z2>
<m2>value 1</m1>
<m1>value 2</m2>
</z2>
...
</Service Response>

混合流程示例在请求和响应 XML 文档中定义的输入为 (x1, y1, x2, y2),而输出为 (z3)。

有人建议采用支持 Ontology 的 SRR(ontology-enhanced SRR,OSRR)方法(有关更多信息,请参阅参考资料)来重新构建语义 Web 服务体系结构。OSRR 方法建议语义 Web 服务应:

  • 具有自描述性
  • 基于文档,而不是基于对象
  • 面向含义或语义,而不是面向名称和语法
  • 服务领域和功能目的独立
  • 模拟人的语义和行为
  • 是使用一个 接口的标准化 Web 服务,其高效性经过了验证
  • 可回避将 WSDL 用于语义描述的需要
  • 实现经过简化,可在当前基础结构(UDDI、WSDL、SOAP)内对 Web 服务进行发现、匹配、组合和调用

所建议的新方法提出,每个 Web 服务都具有两个组件:一个是 XML 文档 (ServiceDescription.xml),另一个是标准化接口——函数 getService(String request): String response。服务提供程序必须在 ServiceDescription.xml 中提供详细的信息和指南,该文件中至少应包含五个生成块(有关更多信息,请参阅参考资料):

  1. 服务领域和功能类别描述
  2. 服务请求输入 XML 文档的格式
  3. 服务响应输出 XML 文档的格式
  4. 服务请求输出要求:定义服务请求的模板
  5. 服务响应输出原型:定义服务响应的模板

Web 服务可以按照服务领域和功能类别进行有意义的分类。一方面,服务描述文件 (ServiceDescription.xml) 是采用考虑语义的方式构建的,以便每个人都能理解所提供的服务的含义,而且此类 XML 文档仍然可由计算机处理。这样就可以对一系列 Web 服务进行标识和自动化,从而可以方便而高效地进行智能服务发现和匹配(每个服务 1-10 毫秒,具体取决于请求程序提供的细节,如服务领域和子域、功能类别和子类别、输入变量规范、预期输出格式)。另一方面,服务描述文件提供了详细的指南,以便用户和计算机能够了解如何使用和处理该服务,并了解服务输出的预期内容。这样,服务组合几乎就是一个复制-粘贴过程,而服务调用也通过仅使用一个接口而得到了简化。

对于服务提供程序,如果采用建议的新方法构建 Web 服务,仍然可以采用传统的 OOP 方法开发此类 Web 服务。唯一的要求就是,所有输入变量都在作为请求程序发送的服务请求的 XML 文档中传输,并从中进行提取。服务提供程序要全面负责在服务链中对所有相关的 Web 服务进行聚合,而此类流程不应向服务请求程序公开。服务器接收到用户的服务请求后,其主要任务是,首先处理 XML 文档,然后确定请求了哪些服务和函数。服务器必须随后检查输入变量是否符合要求,然后调用对应的服务和函数,获取结果,并将结果放入缓存或队列中。它必须最终采用相同的方式执行所有涉及到的服务,并最后生成响应 XML 文档,再将该文档发送回请求程序。为了调用 Web 服务函数,强烈建议在服务提供程序和服务请求程序间交换有意义的 XML 文档,而不要传递经过封装的对象。只有这样,Web 服务才对广大的普通大众有意义,而不再局限于计算机专业人士。

为了实现此方法,应该形成各个领域关于服务描述的特定标准(应在服务请求中描述的内容以及服务响应中可能包含的内容)。图 3 显示了创建服务领域 Ontology 框架的示例。这些提供进行机票、酒店和出租车预订服务的服务提供程序共享相同的服务描述。通常,此类服务包含一个搜索函数和一个预订函数。服务请求程序首先搜索相应的信息,一旦确定了特定的服务,请求程序将发送一个预订请求。该示例显示了这三个服务的搜索函数的唯一服务描述模板。

如果一个服务提供程序 (SP) 希望提供聚合所有三种服务的集成服务,它将在第一个生成块中作为“服务领域和功能类别描述”指定的服务注册表中进行此操作。在服务请求描述模板中,SP 将指定条件型输入要求。即,SP 必须事前在服务描述中指定此类要求,以便服务请求程序可以仅使用 SP 的部分服务来预订机票、酒店,也可以使用所有服务进行操作。因此,服务请求将是有条件的(OWL-S 和 WSMO 具有条件型输出,也能产生相应的效果,但并不采用条件型输入)。SP 接收到服务请求时,它将处理请求 XML 文档,以标识所请求的服务,然后处理相应的请求并返回给请求程序。在此过程中,SP 可能会联系航空公司、酒店和其他公司提供的多个 Web 服务,但原始请求程序永远不会知道这些流程的细节。通过这种方式,服务描述就可以与服务实现分隔开来。开发人员可以在项目实现前首先开发服务描述文件,而项目实现将在完成服务描述文件后直接进行开发。

图 3. 示例服务领域分类和功能类别分析
域
域

当服务请求传输到服务提供程序时,与提供程序的实现有直接关系的唯一元素就是请求中指定的 Function 元素。服务提供程序将据此基于特定的请求来检查将要处理哪些函数(实际上,函数元素可能并不表示编程代码中所使用的函数的实际名称)。对于以下示例,如果酒店预订的所有服务提供程序共享一个协议,则都应了解如何处理此类请求,并可以同时开发各自的应用程序(在必要时):

清单 1. 工作流
<Service Request>
 <Service Name="SearchHotelInformation">
  <Function Name="WebService.Hotel.SearchInformation">
   <InputVariables>
    <City></City>
     <State></State>
   </Location>
   <CheckInDate></CheckInDate>
   <CheckOutDate></CheckOutDate>
   <NumberOfCustomer></NumberOfCustomer>
   <RoomType></RoomType>
  </InputVariables>
 </Service>
<ServiceRequest>
图 4. 动态自动服务发现、匹配、组合、解释和调用
工作流程图
工作流程图

Shi 和 Jagannathan (2005) 提供了采用 OSRR 方法构建的详细示例服务描述文件。图 4 显示了采用 OSRR 方法设计和实现的试验项目。通过单击按钮 Classify Services,此试验项目将演示 Web 服务可以如何基于 ServiceDescription.xml XML 文档进行归类,这些 XML 文档支持自动服务发现和匹配。通过此方法,这个演示系统将处理某些信息,以“了解”是否可以从 Web 服务列表中标识必要的服务和函数。其方法是,在用户键入西弗吉尼亚的地名(如 Morgantown)并单击页面上的两个按钮之一(Get Longitude/LatitudeConvert to UTM X,Y)后,添加或删除网页上的文本框中的 Web 服务。计算机然后可以了解请求是否可以进行处理。如果计算机可以找到必要的服务,则将组合服务请求,按照顺序调用服务,并显示结果。如果无法标识请求的特定服务,计算机将不处理请求,而将直接响应,告知请求程序所缺少的内容。事实上,此位置服务应是 ESRI 通过其 ArcWeb 服务接口提供的 Web 服务,而数据转换服务应是 Microsoft 的 TerraService 提供的 Web 服务。在使用 OSRR 方法时,请求程序并不需要知道流程细节,但可以了解服务可以提供哪些功能以及如何实现服务。

Uschold (2003) 认为语义 Web 是这样的,“协议涉及的方面越多,采用计算机可处理的语义的必要性就越小”(即,语义派生自计算机可处理的逻辑)。可以将这个定理应用于创建语义 Web 服务时,不过我们需要的是更多的标准,而不是更多的逻辑。单个领域中的 Web 服务(如酒店预订服务)至少应有行业协议和标准来指定要求的输入变量和可能的输出格式。实现 OSRR 要求使用此类协议和标准。另一方面,有人认为获得此类协议非常困难,因此 OSRR 也不可能。不过,对于每个特定的服务领域,如果此类简单协议无法实现,用户又如何解码使用逻辑匹配或猜测不同服务提供程序间的关系时的无限可能性呢?由于每个服务并不包含关于其他可用服务的信息,因此编程人员设计了逻辑模型来对聚合服务进行链接和处理。未知服务的问题存在于开发过程中,因为每个开发人员为在 OOP 中使用的服务、函数、输入变量和输出结构使用了各种不同的名称,因此,每个服务提供程序的每个 WSDL 文件都不相同,不过服务的内容可以完全一样。在此情况下,我们需要进行逻辑演绎,以了解编程过程中使用的不同元素间的关系。不过,由于任何逻辑模型都无法枚举出所有可能性,一旦任何开发人员使用不同的方法定义了一个系统,则此类模型将无法使用。即使存在很小的差别,也会造成不匹配的情况。如果所有开发人员都遵循逻辑模型指定的相同规则,则就是在为保持框架正常工作而遵循一个协议或标准。

Uschold (2003) 提出这个问题,“计算机(即软件代理)如何了解某个从来没有遇到过的术语的含义,以便能完成其任务呢?”虽然“语义(无论是隐式的、非正式的或正式的)可以硬连接到 Web 应用程序中”,但允许计算机处理语义以动态地发现内容的含义以及如何使用它,这一目标“可能完全不能实现”。如果对语义 Web 而言,此类说法成立的话,那么对语义 Web 服务也是如此。我们需要更多的协议和标准,而不是逻辑模型,原因是,如果服务开发人员可以事前获得协议,此类用于服务集成的逻辑并不必要,因为协议和标准可以指定所有方面的内容。当考虑到请求程序没有兴趣、关系或责任了解服务聚合的过程时,这种说法是完全成立的。

通过 HTTP/POST 进行 Internet/分布式计算

当可以通过交互 XML 文档调用 Web 服务时,WSDL/SOAP 就不再是必需的组件了,因为我们可以通过 HTTP/POST 执行相同的任务(虽然这两个协议可以共存,而不会发生冲突)。HTTP/POST 的优势在于,为了部署此类语义 Web 服务,人们不需要使用任何特定的软件来编写代码,以通过函数接口(如函数 getService (String request): String response)调用 SOAP Web 服务,因为只需要通过“剪切-复制-粘贴”就可以通过网页执行相同的服务和函数。不过,为了执行用于特定任务的更复杂的系统集成,专业编程人员必须使用特定的软件来在不了解组合流程内的细节的情况下处理不同 Web 服务的请求和响应 XML 文档,从而通过 WSDL/SOAP 或 HTTP/POST 将请求和响应集成到一起。

这是此项尖端技术的必然发展方向。最初,计算机科学家创建 Web 服务技术来通过序列化过程和 SOAP 协议交换各种业务逻辑、对象数据类型和功能,从而解决不同操作系统和编程语义间的互操作性问题。实际上,SOAP 中交换的消息只是包含用于进行计算的序列化信息的 XML 文档,并没有语义方面的意义。现在,当可以在 XML 文档中显式描述和定义服务请求和响应时,此类请求和响应就能直接由网页中的 POST 方法通过 HTTP 进行传输,如图 5 中所示。

图 5. 通过 WSDL/SOAP 的 Web 服务与通过 HTTP/POST 的 Web 服务的对比
工作流程图
工作流程图

演示

为了演示可通过 HTTP/POST 实现 Internet/分布式计算来执行与 WSDL/SOAP 所提供的功能相同的功能,我们使用 .Net 创建了一个 HTTP 服务器。此 HTTP 服务器接受通过 HTTP 用 POST 方法发送的消息,以执行一个地理数据处理函数,从而创建新缓冲区 Polygon 轮廓,其在输入地理位置点范围的 100 米以内。ESRI ArcGIS Server 9.0 用于处理缓冲区函数。

此服务设计为通过 HTTP POST 请求接受 XML 文档。当服务器获得请求时,它将调用 ArcGIS Server 来处理输入数据并生成 XML 文档,然后将其发送回请求程序。

客户端接口是使用 .NET、Java Servlet/JSP 和 CGI/Python 开发的,可以与 .NET HTTP 服务器进行通信。所有三个客户机应用程序都会接收到完全相同的结果,而不会存在任何互操作性问题。

  1. GIS HTTP 服务器
    HTTP 服务器等待通过 POST 方法发送的消息来调用与 bufferPoint 相同的函数,且将相应链接放置在 Address 框中时,只能看见以下错误消息:
    .ArgumentOutOfRangeException: Length cannot be less than zero. Parameter name:
    at System.String.Substring(Int32 startIndex,Int32 length) ad MSDNHandlers.getService.createBuffer
  2. .NET 客户机
    图 6 显示了 .NET 客户机用于与 GIS HTTP 服务器进行交互的 Web 接口:
    图 6. .NET 客户机用于与 GIS HTTP 服务器进行交互的 Web 接口
    .NET 客户机用于与 GIS HTTP 服务器进行交互的 Web 接口
    .NET 客户机用于与 GIS HTTP 服务器进行交互的 Web 接口
  3. CGI/Python 客户机
    图 7 显示了 CGI/Python 客户机用于在服务调用 与 GIS HTTP 服务器进行交互的 Web 接口:
    图 7. CGI/Python 客户机用于在服务调用与 GIS HTTP 服务器进行交互的 Web 接口
    CGI/Python 客户机用于在服务调用前与 GIS HTTP 服务器进行交互的 Web 接口
    CGI/Python 客户机用于在服务调用前与 GIS HTTP 服务器进行交互的 Web 接口

    图 8 显示了 CGI/Python 客户机用于在服务调用 与 GIS HTTP 服务器进行交互的 Web 接口:
    图 8. CGI/Python 客户机用于在服务调用与 GIS HTTP 服务器进行交互的 Web 接口
     CGI/Python 客户机用于在服务调用后与 GIS HTTP 服务器进行交互的 Web 接口
    CGI/Python 客户机用于在服务调用后与 GIS HTTP 服务器进行交互的 Web 接口


    清单 2 显示了 SendHTTPRequest.py 的源代码:
    清单 2. Python 源代码:SendHTTPRequest.py
    import urllib
    import cgi, string, sys
    
    form = cgi.FieldStorage()
    
    inputURL = form["ServerURL"].value
    inputRequest = form["request"].value
    
    f = urllib.urlopen(inputURL, inputRequest)
    s = f.read()
    
    print
    print 'Status: 200 OK'
    print 'Content-type: text/html'
    print
    
    print '<html><head><title>Web Service via Http/Post by Python</title></head>'
    print '<body>'
    print '<br><br><center><b>Here is your request (left) 
    	and response (right)</b></center><br><br>'
    print '<center>'
    print '<div><TEXTAREA name=\"request\" COLS=60 ROWS=40>' 
    	+ inputRequest + '</TEXTAREA>'
    print '<TEXTAREA name=\'request\' COLS=60 ROWS=40>' + s
    print '</TEXTAREA>'
    print '</center>'
    print '</body></html>'
  4. SP/Java Servlet 客户机
    图 9 显示了 JSP/Java Servlet 客户机用于在服务调用 与 GIS HTTP 服务器进行交互的 Web 接口:
    图 9. JSP/Java Servlet 客户机用于在服务调用与 GIS HTTP 服务器进行交互的 Web 接口
    JSP/Java Servlet 客户机用于在服务调用前与 GIS HTTP 服务器进行交互的 Web 接口
    JSP/Java Servlet 客户机用于在服务调用前与 GIS HTTP 服务器进行交互的 Web 接口


    清单 3 是由 JSP/Java Servlet 通过 HTTP/POST 调用的语义 Web 服务的源代码:
    清单 3. JSP/Java Servlet 通过 HTTP/POST 调用的语义 Web 服务
    <html>
    <head>
    <title>HTTP POST vs. WSDL SOAP Web Services
    </title>
    </head>
    <body>
    
    <br>
    <center>
    <h2><P>Semantic Web Service invoked
    	via HTTP/POST by JSP/Java Servlet</h2></P>
    <FORM action=
    	"http://157.182.136.51:8080/HttpApp/servlet/SendHTTPRequest" method="post">
    <P><b><font color="blue">Simple Server URL:
    </font>
    	http://157.182.136.76/xshi/httpPost/demo1/VBHandler.aspx </b></P>
    <P><b><font color="blue">GIS Server URL:
    </font>
    	http://157.182.136.51/agswsprojs/HttpService/getService.aspx </b></P>
    <P><b>(--The GIS Server will create a new buffer feature for any 
    	input point features at your specified distance, e.g. 560 meters.--)</b></P>
    <P><b>(--Sample source data can be copied from <font color="red">
    	http://157.182.136.51/agswsprojs/HttpService/WebForm2.aspx</font>--)</b></P>
    <div><b>Server Requested: </b>
    <input name="ServerURL" type="text" size="80"/></div>
    <br>
    <div><TEXTAREA NAME="request" COLS=60 ROWS=25></TEXTAREA> 
    <TEXTAREA NAME="response" COLS=60 ROWS=25></TEXTAREA></div>
    
    <br>
    <b>Request</b>
    <INPUT TYPE=SUBMIT VALUE="Submit Request">
    <b>Response</b>
    
    </FORM>
    <b><P><font color="red">Before click the button, 
     specify the distance in the BufferDistance tags to c
     reate the new buffer feature of the input dataset
    </font></P></b>
    
    </center>
    </body>
    </html>


    图 10 显示了 JSP/Java Servlet 客户机用于在服务调用 与 GIS HTTP 服务器进行交互的 Web 接口:
    图 10. JSP/Java Servlet 客户机用于在服务调用与 GIS HTTP 服务器进行交互的 Web 接口
    JSP/Java Servlet 客户机用于在服务调用后与 GIS HTTP 服务器进行交互的 Web 接口
    JSP/Java Servlet 客户机用于在服务调用后与 GIS HTTP 服务器进行交互的 Web 接口


    清单 4 是 SendRequestServlet.java 的 Java 源代码:
    清单 4. Java 源代码:SendRequestServlet.java
    import java.io.*;
    import javax.servlet.*;
    import java.util.*;
    import java.net.*;
    import java.io.IOException;
    import java.io.PrintWriter;
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.http.*;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class SendRequestServlet extends HttpServlet {
    
        public void doPost(HttpServletRequest request,
                           HttpServletResponse response)
            throws IOException, ServletException
        {
    
            String strRequest = request.getParameter("request");
            String strURL = request.getParameter("ServerURL");
    
            PrintWriter writer = response.getWriter();
            response.setContentType("text/html");
            writer.println("<html>");
            writer.println("<head>");
            writer.println("<title>Your requested service is returned</title>");
            writer.println("</head>");
            writer.println("<body bgcolor=\"white\">");
    
    		 String newRequest1, newRequest2, newRequest3;
    		 newRequest1 = strRequest.replaceAll("<","<");
    		 newRequest2 = newRequest1.replaceAll(">",">");
    		 newRequest3 = newRequest2.replaceAll("\"",""");
    
    		 writer.println("<br><br><center><b>Here is your request (left) and
    response
    		 		 		 (right) </b></center><br><br>" );
    		 writer.println("<center>");
    		 writer.println("<div><TEXTAREA name=\"request\" COLS=60 ROWS=40>" 
    		 		 		 + newRequest3 + "</TEXTAREA>");
    		 String outPut;
    		 outPut="";
    
    		 try
    		 {
    		     URL		 url;
    		     URLConnection urlConn;
    		     DataOutputStream printout;
    		     DataInputStream input;
    
    		     url = new URL (strURL);
    		     urlConn = url.openConnection();
    		     urlConn.setDoInput (true);
    		     urlConn.setDoOutput (true);
    		     urlConn.setUseCaches (false);
    		     urlConn.setRequestProperty
    		     ("Content-Type", "application/x-www-form-urlencoded");
    
    		     printout = new DataOutputStream (urlConn.getOutputStream ());
    
    		     String content = strRequest;
    		     printout.writeBytes (content);
    		     printout.flush ();
    		     printout.close ();
    
    		     input = new DataInputStream (urlConn.getInputStream ());
    
    		     String str;
    		     while (null != ((str = input.readLine())))
    		     {
    		 		 System.out.println (str);
    		 		 outPut = outPut + str + "\n";
    		     }
    
    		     input.close ();
    
    		 }
    		 catch (MalformedURLException me)
    		 {
    		 		 System.err.println("MalformedURLException: " + me);
    		 }
    		 catch (IOException ioe)
    		 {
    		 		 System.err.println("IOException: " + ioe.getMessage());
    		 }
    
    		 String newResponse1, newResponse2, newResponse3;
    		 newResponse1 = outPut.replaceAll("<","<");
    		 newResponse2 = newResponse1.replaceAll(">",">");
    		 newResponse3 = newResponse2.replaceAll("\"",""");
    
            writer.println("<TEXTAREA name=\"request\" COLS=60 ROWS=40>" +
    newResponse3 
            		 		 + "</TEXTAREA> </div>");
            writer.println("</center>");
            writer.println("</body>");
            writer.println("</html>");
    
        }
    
    }
  5. .NET Studio
    图 11 显示了在 .NET Studio 中开发的一个普通 Web 服务。它提供了一个 WSDL 接口来接受两个输入变量,一个是 String 数据类型的 strPoint 和一个 Double 数据类型的 dblDistance。输出结果是一个 String 数据类型。此函数将以输入点为基础,按照用户指定的距离创建一个新缓冲区轮廓数据层。在实际中,此函数可用于以下情况:公司希望知道其业务中心(点轮廓) 5000 米(或任何其他数字)范围内的服务覆盖情况。
    图 11. 在 .NET Studio 中开发的 Web 服务
     在 .NET Studio 中开发的 Web 服务
    在 .NET Studio 中开发的 Web 服务
  6. bufferPoint Web 服务
    清单 5 提供了 bufferPoint Web 服务的 WSDL 文档的代码:
    清单 5. bufferPoint Web 服务的 WSDL 文档

    <?xml version="1.0" encoding="utf-8" ?> - <wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://tempuri.org/geoWebService/Service1" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://tempuri.org/geoWebService/Service1" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> - <wsdl:types> - <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/geoWebService/Service1"> - <s:element name="bufferPoint"> - <s:complexType> - <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="strPoint" type="s:string" /> <s:element minOccurs="1" maxOccurs="1" name="dblDistance" type="s:double" /> </s:sequence> </s:complexType> </s:element> - <s:element name="bufferPointResponse"> - <s:complexType> - <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="bufferPointResult" type="s:string" /> </s:sequence> </s:complexType> </s:element> <s:element name="string" nillable="true" type="s:string" /> </s:schema> </wsdl:types> - <wsdl:message name="bufferPointSoapIn"> <wsdl:part name="parameters" element="tns:bufferPoint" /> </wsdl:message> - <wsdl:message name="bufferPointSoapOut"> <wsdl:part name="parameters" element="tns:bufferPointResponse" /> </wsdl:message> - <wsdl:message name="bufferPointHttpGetIn"> <wsdl:part name="strPoint" type="s:string" /> <wsdl:part name="dblDistance" type="s:string" /> </wsdl:message> - <wsdl:message name="bufferPointHttpGetOut"> <wsdl:part name="Body" element="tns:string" /> </wsdl:message> - <wsdl:message name="bufferPointHttpPostIn"> <wsdl:part name="strPoint" type="s:string" /> <wsdl:part name="dblDistance" type="s:string" /> </wsdl:message> - <wsdl:message name="bufferPointHttpPostOut"> <wsdl:part name="Body" element="tns:string" /> </wsdl:message> - <wsdl:portType name="Service1Soap"> - <wsdl:operation name="bufferPoint"> <wsdl:input message="tns:bufferPointSoapIn" /> <wsdl:output message="tns:bufferPointSoapOut" /> </wsdl:operation> </wsdl:portType> - <wsdl:portType name="Service1HttpGet"> - <wsdl:operation name="bufferPoint"> <wsdl:input message="tns:bufferPointHttpGetIn" /> <wsdl:output message="tns:bufferPointHttpGetOut" /> </wsdl:operation> </wsdl:portType> - <wsdl:portType name="Service1HttpPost"> - <wsdl:operation name="bufferPoint"> <wsdl:input message="tns:bufferPointHttpPostIn" /> <wsdl:output message="tns:bufferPointHttpPostOut" /> </wsdl:operation> </wsdl:portType> - <wsdl:binding name="Service1Soap" type="tns:Service1Soap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> - <wsdl:operation name="bufferPoint"> <soap:operation soapAction= "http://tempuri.org/geoWebService/Service1/bufferPoint" style="document" /> - <wsdl:input> <soap:body use="literal" /> </wsdl:input> - <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> - <wsdl:binding name="Service1HttpGet" type="tns:Service1HttpGet"> <http:binding verb="GET" /> - <wsdl:operation name="bufferPoint"> <http:operation location="/bufferPoint" /> - <wsdl:input> <http:urlEncoded /> </wsdl:input> - <wsdl:output> <mime:mimeXml part="Body" /> </wsdl:output> </wsdl:operation> </wsdl:binding> - <wsdl:binding name="Service1HttpPost" type="tns:Service1HttpPost"> <http:binding verb="POST" /> - <wsdl:operation name="bufferPoint"> <http:operation location="/bufferPoint" /> - <wsdl:input> <mime:content type="application/x-www-form-urlencoded" /> </wsdl:input> - <wsdl:output> <mime:mimeXml part="Body" /> </wsdl:output> </wsdl:operation> </wsdl:binding> - <wsdl:service name="Service1"> <documentation xmlns="http://schemas.xmlsoap.org/wsdl/" /> - <wsdl:port name="Service1Soap" binding="tns:Service1Soap"> <soap:address location="http://157.182.136.51/agswsprojs/geoWebService/Service1.asmx" /> </wsdl:port> - <wsdl:port name="Service1HttpGet" binding="tns:Service1HttpGet"> <http:address location="http://157.182.136.51/agswsprojs/geoWebService/Service1.asmx" /> </wsdl:port> - <wsdl:port name="Service1HttpPost" binding="tns:Service1HttpPost"> <http:address location="http://157.182.136.51/agswsprojs/geoWebService/Service1.asmx" /> </wsdl:port> </wsdl:service> </wsdl:definitions>

如果服务描述可以采用新方法组成,则没有计算机知识背景的人就可以方便地了解和找到所需的特定服务,这些服务是根据服务领域、功能类别进行分类的,并包含有关输入和输出目标的详细规范。

挑战和结束语

Tim Berners-Lee 定义语义 Web 时,“语义”表示“可由计算机进行处理”。既然 Web 服务设计为可由计算机进行处理,WSDL 就被批评为 具有语义。虽然很多人声称语义 Web 服务的定义可以为语义 Web 加 Web 服务,但 Veltman (2004) 却总结道“与含义无关的语义 Web 可能应该更准确地称为事务 Web 或逻辑 Web”,因为设计语义 Web 的人员认为“逻辑含义是含义的唯一客观面;所有其他含义都是主观的,因此并不重要。就这点来看,语义 Web 实际上将自己限制在了逻辑领域中。在科学、技术和业务方面,这一点可以带来非常实际的效果”(Veltman,2004)。

Uschold (2003) 撰写了一篇文章“Where Are the Semantics in Semantic Web?”(语义 Web 中的语义位于何处?)现在的问题是“语义 Web 服务中的语义位于何处?”如果语义 Web 的目标是允许计算机处理语义、动态发现内容含义以及如何按照逻辑方法使用它,则“可能完全不能实现”(Uschold,2003)。也不可能使用逻辑建模方法开发语义 Web 服务。

McCool (2005) 总结道“逻辑是 OWL 的基础,但受到无法表示规则的例外情况及其有效的场景的限制。”“因为它具有复杂的格式,要求用户牺牲表现度,并在转换和维护方面投入大量成本,语义 Web 将永远不会得到广泛的应用。”“新的表示形式必须能够方便地与自然语言进行转换。任何其他方法都忽略了表示方面的问题,均假定不会受到环境限制且逻辑规则就已足够了,此方法将会失败。语义 Web 将失败 的原因在于,它继承了这些问题,然后又给 Web 带来更多的问题,而 Web 代表人类知识的广度,因此它将失败。”如果所谓的语义 Web 技术已经失败了,并且将来还会失败,那么研究人员和开发人员就应探索采用其他方式来实现语义 Web 服务的目标。

正如所演示的,数据和服务语义无法在 WSDL 文件中标识,在 WSDL 文件中包含服务、函数、输入和输出的名称,也包含 OOP 中使用的各种对象间的关系。WSDL 的内容并不是服务的含义,服务集成的聚合过程也不是服务的含义。构建于逻辑模型上用于链接服务的方法并不会向服务请求程序提供解决方案和答案,相反,此类基于逻辑的方法增加了服务请求程序获取必要信息来了解和部署复杂框架从而获得结果的难度和负担。此类复杂系统意味着只有一部分具有这方面专业知识的计算机专业人士才能使用这个系统,而其他所有人都无法理解在这些框架中使用的相关术语和逻辑。如果大多数人无法使用此技术,这就不是成功的技术发展,反而加剧了全球的“数字分水岭”危机。

将所有组合 Web 服务转换为原子过程可以算作开发语义 Web 服务的短期目标。Ontology 增强语义请求和响应方法将重点放在服务请求程序和提供程序间的基本关系上,并将数据和服务语义嵌入到了单个描述文档中。请求程序只需要遵循服务请求和响应的详细语义模板,就可以部署服务,而无需处理服务聚合的组合过程。语义 Web 服务中的语义位于何处?到目前为止,可得出结论,语义 Web 服务中的语义可以全部在 ServiceDescription.xml XML 文档中进行定义和标识。通过对此类 XML 进行分析,计算机可以方便地根据不同的服务领域/子域和功能类别及子类别对 Web 服务进行分类,而同时可以在变量级预定义和释放所有服务调用要求。这种优势将帮助请求程序更有效而准确地标识所需的服务。虽然 UDDI 框架需要进行相应的更改,但服务注册和标识过程将非常方便,因为,只要服务注册了,UDDI 系统就可以方便地处理 ServiceDescription.xml XML 文档,以进行详细的分类和初始匹配,从而请求程序可以方便地定位特定的服务。

建议应由 W3C 或 ISO 等标准化组织对服务领域和子域需求的 Ontology 定义进行分类,而各个领域特定的服务语义则必须通过在实践和应用中采用自底向上方法来确定和开发。挑战在于,在不同的服务领域中进行协商和沟通,以生成协议和标准来描述各个领域特定的 Web 服务。描述服务流程并不能说明语义,因为请求程序将只希望得到其请求对应的答案,而不需要接收一个复杂的框架来据此派生出答案。正如在本文前面讨论的,通过逻辑推理创建语义 Web 服务几乎是不可能的。研究人员应该将更多的注意力放在对各个领域特定的服务描述进行标准化的工作上。对于每个特定的服务领域,如果无法获得此类简单的协议,则在使用逻辑确定、匹配或猜测不同服务提供程序之间的关系时,要合理地处理其无限未知的可能性会更困难,或者根本不可能。目前,所有成功的可互操作应用程序和开发都是基于此类标准或 HTTP、HTML、WSDL 之类的协议,而不是以任何逻辑为基础的。为了实现语义 Web 服务,长期的目标是,必须采用自顶向下或自底向上的方法形成更多的标准和协议。

致谢

作者感谢 Greg Elmes 博士帮助审阅本文,并提出了一些有用的建议。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services, XML
ArticleID=141824
ArticleTitle=使用 HTTP/POST 进行 Internet/分布式计算
publish-date=06262006