提高 J2EE 技术和 .NET 之间的互操作性,第 1 部分

本文探讨了跨平台Web服务集成的互操作性问题,分析了J2EE技术和.NET技术之间的交互难题,提出了最佳实践建议,并通过案例展示了RPC/encoded方式的优缺点。
将 Web 服务组织在一起来创建跨组织的商业过程,要求所有的参与者以同样的标准模型进行编程并且避免提供专有的实现。经过多年来的众多厂家之间在标准协议上来提高互操作性的一致努力,在这方面已经取得了明显的进步。然而,实现 Web 服务间的无缝交互这一终极目标仍然是倍受关注的热门话题。研究了跨平台 Web 服务集成所面临的几个常见的互操作问题的根源,作者分析认为多个互操作问题都是源于 J2EE 技术和 .NET 之间的交互类型、基本数据类型和结构以及命名空间问题。 Wangming Ye 提供了最佳实践使得您可以避免这些问题,并提高成功集成的几率。本系列文章的第 1 部分着重强调了 WSDL 设计的重要性,并分析了 Web 服务集成中传统的 RPC/encoded 方式的优缺点。

引言

Web 服务希望并且承诺将分散的应用程序以一种无缝的方式进行集成。但企业应用程序是在不同的平台上采用不同的技术构建的,因此,跨业务的集成并不是一件轻而易举的事。最近出现的基于 Web 服务的业务流程执行语言(BPEL)为定义 Web 服务的行为提供了一个高层描述语言。它提供一个标准和可移植的语言来将多个 Web 服务融合到一个商业流程中。 由于 BPEL 受到一些主要厂商的欢迎,一些用来自动设计商业流程的集成开发工具,如 IBM® WebSphere® Studio Application Developer Integration Edition (Application Developer) 已经进入市场。这些工具减少了 Web 服务集成时需要的大量编码工作,并允许您通过拖拽 WSDL 文件到工具中的方式构建商业流程。我们期望这些工具能够自动产生调用 Web 服务的客户端的存根。 因此,集成的成功目前很大程度上一来于底层复杂的工具支撑。这就更加要求开发成员采用最佳实践来确保共享 Web 服务的内在互操作性。

正如我在本文以及随后的文章中所讲的,以下几个问题需要特别关注:

  • 利用厂商工具根据实现代码得到 WSDL 中的 Web 服务语言非常方便。但是这种方式忽略了消息脚本的设计,但这一点正是异构的环境(如 J2EE 技术对.NET)中 Web 服务互操作的关键。
  • 流行的 RPC/encoded 方式以其简单、灵活且熟悉的特点对开发人员是个有吸引力地选择。然而,在厂商之间对抽象 SOAP 编码数据模型的同步实现带来的困难成为 Web 服务互操作的一个困难性的挑战。
  • 弱类型的集合对象、包含空元素的数组和特定的本地数据类型都给互操作性带来一定的问题,特别是:
    • 对于厂商提供的工具来讲,准确地解释代表弱类型集合对象的 XML 脚本并将它们映射到本地数据类型是不可能的。
    • 带有空元素的数组的 XML 表示在 .NET 和 Websphere 之间是不同的。
    • 由于本地数据类型和 XSD 数据类型并不具有一一对应关系,在转换过程中,信息或精度将会丢失。
  • 由于使用相对 URI 引用,Java 技术和 .NET之间不同命名转换将会导致命名空间冲突。




回页首


从 WSDL 开始

所有 Web 服务的互操作问题都可以归结到 WSDL 文件上。WSDL 是 Web 服务的接口定义语言(IDL)和服务器与客户间的协议。服务语法,即 WSDL 文件中的消息类型、数据类型和交互方式,是构建松耦合的系统的关键。尽管 WSDL 并不要求使用特定的类型系统, XML 脚本数据类型(XSD)在 Web 服务领域内广受欢迎。

XSD 提供了大量的内置的基本类型并允许服务提供者进一步定义定制的复杂类型。 XSD 的类型系统比任何编程语言的类型系统都更加复杂和强大,并且更重要的是,它是独立于编程语言的,因此,它成为 WSDL 定义 Web 服务语法的逻辑起点。

理论上讲,就像 COM 和 CORBA 的 IDL,您应该首先创建并编辑 WSDL --在您基于 WSDL中的服务语法来构建 Web 服务和特定实现语言的客户程序之前,定义接口、消息和数据类型。

Web 服务互操作的最佳实践总结
  • 首先设计 XSD 和 WSDL,然后基于脚本和接口进行编程。
  • 只要有可能,就避免使用 RPC/encoded 方式。
  • 用固定类型的简单数组包装任何弱类型集合对象,来作为 Web 服务方法的参数。
  • 在 Web 服务和 客户程序之间避免传递带有空元素的数组。
  • 在 Web 服务的方法中不要提供无符号数值数据类型,可以采用包装方法来提供并转换数据类型。
  • 当映射 XSD 类型到某一语言的值类型和另一语言的引言类型时要特别小心。定义一个复杂类型来包装值类型并通过设置复杂类型为空来表示空值。
  • 由于基础 URI 在 WSDL 文档中没有良好定义,避免在命名空间声明中使用相对 URL 引言。
  • 为避免在厂商间不同命名转换产生的冲突,将所有的 Web 服务以一个统一的域名进行限定。一些工具提供命名空间的定制映射或提供包名的重构来解决这一问题。
  • 为Web 服务互操作组织(WS-I)一致性检查开发一个复杂的测试包。

然而,事实上,即使经验丰富的都不会那么做。他们最初开始实现特定于语言的接口,如 Java 接口,然后依赖厂商的工具从它们的实现代码中获取 WSDL 服务语法;再把这些 WSDL 服务语法交给 Web 服务客户程序以便根据 WSDL 找出如何来调用 Web 服务。最后,在他们经常不需要知道 WSDL 之前, 他们就能够创建并运行复杂的 Web 服务。他们可能在异构的环境中运行他们的客户和服务器程序,即或者 J2EE 平台,或者 .NET 平台,或者二者都有。

目前厂商的集成开发工具下面的方向发展,即所有的东西,包括 WSDL都可以自动生成,不需要写任何代码(或者,至少许多厂商都声称并希望如此)。这一趋势很有吸引力,因为它开发效率很高,且相对于人工编码它很少出错(当然在工具功能合适且可靠的条件下)。在单一的平台上,互操作性一般不成问题,只要 Web 服务和客户都使用同样的开发工具。

当对跨越异构的环境产生互操作性要求时,问题变得比较明显。现在,您从实现语言来创建 Web 服务交互的关键构件,然后利用平台独立的工具将它们映射到 WSDL 中语言独立的元素。当客户平台上的工具根据 工具生成的 WSDL 来产生服务代理时,将进行另外一个映射。这一流程基于下面的事实,WSDL 是 Web 服务的接口定义语言。语言独立的 WSDL 应该成为客户和服务器的共同基础。以这种方式使用开发工具将双倍增加映射过程中信息丢失的可能。

这并不意味着您应该放弃这些工具,所有的厂商应该停止生产并在市场上销售这些工具。在当前的企业应用程序开发领域自动化已经成为一个重要的因素。工具本身很强大,关键在您怎么利用它们的力量?您可以利用这些工具产生一个框架 WSDL 来作为起点或者模板。脚本、消息部件和数据绑定必须仔细设计来遵循 WS-I 一致性要求。就像您出于数据库效率的考虑而不希望由工具产生数据库脚本一样,您不应该忽视手工设计高效 Web 服务消息和数据类型。有时,即使元素的名字也要仔细设计来遵循潜在的异构平台间的命名转换。对于 Web 服务互操作性来讲, WSDL 是唯一比较重要的构件。程序员必须学会基于原始 XML 消息进行编程,至少学会读取 XSD、 WSDL 和 SOAP 消息。





回页首


仍旧采用 RPC/encoded?

WS-I基本规范提出采用文本 XML 实现互操作。它禁止对 soap:Envelope 或派生的 soap:Body 元素使用 soap:encodingStyle 属性。因此, RPC/literalDocument/literal 是 WS-I 标准唯一支持的 2 种格式。但是有许多 Web 服务工具不支持 RPC/literal ,因此,它将 Document/literal 作为唯一的实际互操作性标准。

然而,采用 RPC/encoded 方式来处理在 XML 脚本或 WSDL 出现之前就已经存在很久;另外,一些编码规则仍旧不能够采用 XSD 来表述。尽管 SOAP 编码被认为 Web 服务互操作性问题的主要根源,以及 ASP.NET WebMethod 基础设施缺省采用 Document/literal,直到现在,大部分 J2EE Web 服务缺省采用 RPC/encoded 方式。

RPC/encoded 方式受欢迎主要因为对于习惯了远程过程调用惯例的程序员来说是较为简单的编程模型。RPC 绑定方式采用方法名称和参数来产生代表方法调用堆栈的结构,因此,它使 Web 服务看起来像一个带有封装对象的单一的逻辑组件,所有操作都在 SOAP RPC 堆栈中处理。这一点与 Document/literal 方式相反,在那种方式下,程序员不得不处理所有的事务,包括基于 XML 的 SOAP 消息的序列化和逆序列化。

另外,SOAP 编码规则定义了标准,方便了从编程类型到 XML 的映射。编码规则非常灵活并支持图形数据和多态的表示。因此,它毫无疑问比 Document/literal 更适合,后者依赖于自然树结构来表示数据对象。

为了对 RPC/encoded 能做什么、不能做什么有个清晰的概念,让我们看一个示例。 Serializable Person 类定义了一个它自己的友元,如 清单 1所示。


清单 1. 自引用的 Person 类


public class Person implements java.io.Serializable {
	private Person friend;
	private String name;
	
	public Person getFriend() {
		return this.friend;
	}
	public void setFriend(Person friend) {
		this.friend = friend;
	}
<!-- Other setter and getter methods -->
}

如果您初始化 2 个 Person:A 和 B,并使 Person A 成为 Person B 的友元,使 Person B 成为 Person A 的友元,那么您创建了一个循环的对象图:


清单 2. makeFriend 方法



public Person makeFriend(Person A, Person B) {
	A.setFriend(B);
	B.setFriend(A);
	return A;
}

如果您以 Document/literal Web 服务提供 清单 2 中的方法,那么在 XML 中怎样表示循环对象呢?

事实证明利用 Document/literal 方式没有方便的方法来实现这一点。原因在于: Document/literal 方法基于 XML 脚本定义了消息类型作为固定类型。XML 脚本利用 XSD 基本类型作为叶子节点来表示自然树结构。一个循环对象图不能够转换为树结构。在这种情况下,由于 Person 类的示例 A 和 B 之间的循环引用,对象 A 和 B 的序列化成为递归循环。利用 WebSphere Studio ,最终您将看到堆栈溢出意外。这并不意味着 Document/literal 方法没有办法来解决它,但是需要开发人员更加深入的工作。

如果您采用 SOAP 编码作为消息绑定,通过在循环图上应用 SOAP 编码规则的多引用,循环引用可以很简单地表示。通过在 WebSphere 平台上运行 Web 服务来响应将 John 和 Jason 互相成为友元的 SOAP 请求,可以看一下序列化 XML 的载荷。 清单 3显示了循环图是多么容易表示。


清单 3. WebSphere 的 SOAP 编码响应消息


<soapenv:Body soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<makeFriendResponse xmlns="http://cyclic.test">
<makeFriendReturn href="#id0" xmlns=""/>
</makeFriendResponse>
<multiRef  id="id0" soapenc:root="0" 
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns-
520570027:Person" xmlns:ns-520570027="http://cyclic.test" xmlns="">
<name xsi:type="xsd:string">John</name>
<friend href="#id1"/>
</multiRef>
<multiRef  id="id1" soapenc:root="0" 
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns-
520570027:Person" xmlns:ns-520570027="http://cyclic.test" xmlns="">
<name xsi:type="xsd:string">Jason</name>
<friend href="#id0"/>
</multiRef>
</soapenv:Body >

注意 SOAP 怎样使用本地 id0id1 的类型 ID 来对编码元素 Person A (John)Person B (Jason) 定义唯一的标志符,另外一个本地 unqualified 属性 href 用来定义这两个 ID 值的引用。 对象 A对象 B 之间的循环引用通过灵活的 SOAP 编码规则可以很方便地表示。

RPC/encoded 模型的能力对于传统的面向对象的程序员具有巨大的吸引力,它们已经习惯了在单一的领域内编程,如 J2EE 技术或 .NET 。当范围被限定在一个单一的平台 ,SOAP 消息的编写者和阅读者具有同步的存根来理解编码的 SOAP 消息,尽管 RPC/encoded 模型为 Web 服务的设计者和实现者提供了很大的方便。但当 Web 服务跨平台时,它将带来巨大的代价。面对跨平台的互操作的要求,它的强大的能力反而变成弱点。为了说明互操作问题,仔细看一下 Application Developer 从前面 Web 服务生成的 WSDL 文件(参阅 清单 4)。

在 WSDL 文档中, 操作 makeFriend 的输出消息 makeFriendResponse 有一个 Person 类型的消息部件 makeFriendReturn。 根据脚本,复杂类型 Person 必须有一个 xsd:string 类型名字的直接子元素,和一个 Person 类型的友元,并且不能有其它类型和属性。然而,序列化的 SOAP 响应消息( 清单 3)当然不符合这个规则。响应消息中的 href 没有在定义中出现。对于这个问题,怎样告诉接收者元素 A 和 B 是 Person 类的实例呢?

事实上,如果您编写一个 .NET 客户来与这个 Web 服务交互, .NET 客户将不能够正确地逆序列化 清单 3 所示的 WebSphere 响应。.NET 客户期望的描述 John 和 Jason 是友元的响应消息如下所示:


清单 5:来自 .NET 的 SOAP 编码响应消息


<soap:Body 
soap:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/"><tns:makeFriendsResponse>
	<makeFriendsResult href="#id1" />
</tns:makeFriendsResponse>
<types:Person id="id1" xsi:type="types:Person">
	<name xsi:type="xsd:string">John</name>
	<friend href="#id2" />
</types:Person>
<types:Person id="id2" xsi:type="types:Person">
	<name xsi:type="xsd:string">Jason</name>
	<friend href="#id1" />
</types:Person></soap:Body>


正如您看到的,多引用访问器编码是很强大的,但是它很难在 XSD 中表达。因此在不同的平台之间的实现有些细微的不同,如 清单 3 清单 5 所示。 示例说明了 SOAP 编码和 XML 脚本校验之间的不同。 Document/literal 通过文本文档来传递信息并基于 XML 脚本来校验和逆序列化对象的 XML 表示。不同于 Document/literalRPC/encoded 模型使用 SOAP 编码规则来表述抽象 SOAP 数据模型,并依赖厂商的 SOAP 库来提供抽象数据模型的具体实现。它是高度平台独立的。它不需要中间的规范来填补空白,因此,这种方式对于不同厂商的实现是开放的。尽管 SOAP 是标准,由于缺少 XML 脚本支持,SOAP 实现(如 Application Developer V4 和 V5 的基于 DOM 的 Apache SOAP 库和基于 SAX 的 Apache AXIS 库、MS .NET 1.1 框架的 MS SOAP 工具包 )还是有细微不同。





回页首


利用 WS-I 一致性测试工具检测一致性

WS-I 基本规范为遵守 Web 服务规范来构建互操作的 Web 服务提供了指导。 WS-I 工作组开发了 WS-I 一致性测试工具来帮助开发人员判断它们的 Web 服务是否遵循规范指南。WS-I 测试工具分析器可以产生一个规范一致性报告来记录您的 WSDL 文档的一致性声明。目前,测试工具的 C# 和 Java 版本都可以下载。通过运行测试工具,WS-I 基本规范 1.0 的大部分明显的冲突都会被报告。 清单 4 中的 WSDL 文档的一致性检查测试的冲突如 清单 6 中所示。


清单 6. 从 WS-I 测试工具获取的失败报告

Result: failed
Failure Message: The use attribute of a soapbind:body, soapbind:fault, 
soapbind:header and soapbind:headerfault does not have value 
of "literal".
Failure Detail Message:

SOAPBody ({http://schemas.xmlsoap.org/wsdl/soap/}body):
required=null
use=encoded
encodingStyles=[http://schemas.xmlsoap.org/soap/encoding/]
namespaceURI=http://cyclic.test
 
Element Location:
  lineNumber=87

失败消息是自我解释的: soapbind:bodysoapbind:faultsoapbind:headersoapbind:headerfault 中的 "use=encoded" 被认为是不遵守 WS-I 规范的。您应该使用 "use=literal" 来代替。由于毫无疑问要使用 Document/encoded,也就是说,您不应该使用 RPC/encoded 方式。

尽管 WS-I 一致性测试工具并不能够捕捉到 Web 服务互操作相关的所有问题,但它仍旧是个很强大的工具。与开发 Web 服务一起,不断开发复杂的测试工具包来捕捉潜在的互操作问题是一个很好的实践。关于工具的更多详细信息,请参阅 参考文献 部分的文章。





回页首


结束语

作者强调了在真正实现 WSDL 中的 Web 服务语法之前必须仔细设计,解释了为什么 SOAP 编码是 Web 服务互操作的主要障碍,并演示了构建复杂测试包的重要性来测试 WS-I 规范的一致性。在随后的系列文章中,作者将讨论由 inbound 和 outbound 数据类型和命名空间转换而导致的互操作性问题。





回页首


参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值