级别: 初级 Russell Butek (butek@us.ibm.com), 软件工程师, IBM Richard Scheuerle (scheu@us.ibm.com), 软件工程师, IBM
2004 年 5 月 01 日 这个往返系列最后的一个技巧讨论如何使用映射元数据文件来处理往返问题。
最后的往返方面的技巧将向您展示如何使用映射元数据文件来处理往返问题,这个问题是我们在本系列开头的两个技巧中遇到的。请参见
参考资料以获得到这些技巧的链接。
什么是映射元数据文件?
如果您从上一个技巧(请参见
参考资料)中解开 EAR 文件,您将发现一个 WAR 文件。如果您解开这个文件,您将发现一个
WEB-INF/Roundtrip2_mapping.xml文件。该文件包含将 Web 服务描述语言(Web Services Description Language,WSDL)中的 WSDL/XML 构造映射到 WAR 文件中封装的类所需的信息。该文件的格式是由
Web Services for J2EE 规范定义的。
可以由厂商的 WSDL-to-Java 工具生成的映射文件来指定如何将每个 WSDL/XML 构造映射到 Java 代码中。通常,该文件遵循由
JAX-RPC 规范指定的映射。然而,如果厂商支持可选映射,则文件就可能不同于 JAX-RPC 映射。
修改映射元数据文件
如果您想要改变标准 JAX-RPC 映射规则中的映射的话,您可以修改映射元数据文件。可以通过许多方式来修改映射文件。
在 EAR 中直接修改映射文件
您可以修改映射文件,并且利用前面的技巧将其插入到 EAR 文件中。但这是不够的。您可能还必须修改如下文件:web.xml、webservices.xml 和其他属性描述符文件。不要忘记,我们现有的 EAR 文件是一个用包装器包装原始的遗留服务实现的文件。您还应该把不用的包装器类从 EAR 文件中删除。这种方法需要花费大量的工作,并且很有可能会丢失一些东西。
在创建 EAR 文件时修改映射文件
一种更好的方法是,当您最初生成构件时,将修改过的映射文件作为输入提供给您最喜欢的 WSDL-to-Java 工具。通过这种方法,生成的构件从一开始就知道对映射文件的更改,而您不必更改它们。请注意,IBM WebSphere WSDL2Java 工具提供了一种机制来直接提供映射文件。其他厂商的 WSDL-to-Java 可能有不同的方法来修改映射文件。另外,现在还不会生成该文件的包装器版本,因此包装器实现将不是必需的。
表 1描述了为解决前面的技巧中的往返问题需要对映射元数据文件进行的更改。要查看完整的映射元数据文件,请参见
参考资料部分中所包括的 EAR 文件。
表 1 - 修改没有往返的 Java 代码
|
问题
| 我们需要
Date ,而不是
Calendar 。
| |
改正
|
java-xml-type-mapping 构造将
dateTime 映射到
Calendar 类。
<java-xml-type-mapping>
?<java-type>
java.util.Calendar</java-type>
--<root-type-qnamexmlns:rtq="http://www.w3.org/2001/XMLSchema">
----rtq:dateTime
--</root-type-qname>
?<qname-scope>simpleType</qname-scope>
</java-xml-type-mapping>
|
改为将
dateTime 映射到
Date 类。
<java-xml-type-mapping>
?<java-type>
java.util.Date</java-type>
--<root-type-qnamexmlns:rtq="http://www.w3.org/2001/XMLSchema">
----rtq:dateTime
--</root-type-qname>
?<qname-scope>simpleType</qname-scope>
</java-xml-type-mapping>
|
| |
问题
| 我们需要原始的 Life Bean,而不是生成的 Life Bean。 | |
改正
| 可以用
java-xml-type-mapping 将 XML Life complexType 映射到生成的
Life 类。
<java-xml-type-mapping>
?<java-type>
--
com.ibm.developerWorks.roundtrip.generated.Life
?</java-type>
?<root-type-qname?xmlns:rtq="http://roundtrip.developerWorks.ibm.com">
--rtq:life
?</root-type-qname>
?...
</java-xml-type-mapping>
|
将
java-xml-type-mapping 更改为使用原始的
Life 类。
<java-xml-type-mapping>
?<java-type>
--
com.ibm.developerWorks.roundtrip.life
?</java-type>
?<root-type-qname?xmlns:rtq="http://roundtrip.developerWorks.ibm.com">
--rtq:life
?</root-type-qname>
?...
</java-xml-type-mapping>
|
我们还需要更改
getALife 方法的返回值。
?<service-endpoint-method-mapping>
--<java-method-name>getALife</java-method-name>
--<wsdl-operation>GetALife</wsdl-operation>
--<wsdl-return-value-mapping>
--?<method-return-value>
----
com.ibm.developerWorks.roundtrip.generated.Life
--?</method-return-value>
--?<wsdl-message?xmlns:wrvm="http://roundtrip.developerWorks.ibm.com">
----wrvm:GetALifeResponse
--?</wsdl-message>
--?<wsdl-message-part-name>GetALifeReturn</wsdl-message-part-name>
--</wsdl-return-value-mapping>
?</service-endpoint-method-mapping>
|
将返回类的类型更改为原始的
Life 类。
?<service-endpoint-method-mapping>
--<java-method-name>getALife</java-method-name>
--<wsdl-operation>GetALife</wsdl-operation>
--<wsdl-return-value-mapping>
--?<method-return-value>
----
com.ibm.developerWorks.roundtrip.life
--?</method-return-value>
--?<wsdl-message?xmlns:wrvm="http://roundtrip.developerWorks.ibm.com">
----wrvm:GetALifeResponse
--?</wsdl-message>
--?<wsdl-message-part-name>GetALifeReturn</wsdl-message-part-name>
--</wsdl-return-value-mapping>
?</service-endpoint-method-mapping>
|
| |
问题
| 该接口应该是原始的
population ,而不是生成的
population 。
| |
改正
|
service-endpoint-interface-mapping 构造定义了该接口。
<service-endpoint-interface-mapping>
?<service-endpoint-interface>
--
com.ibm.developerWorks.roundtrip.generated.Population
?</service-endpoint-interface>
?<wsdl-port-type?xmlns:seimwpt="http://roundtrip.developerWorks.ibm.com">
--seimwpt:population
?</wsdl-port-type>
?<wsdl-binding?xmlns:seimwb="http://roundtrip.developerWorks.ibm.com">
--seimwb:populationSoapBinding
?</wsdl-binding>
?<service-endpoint-method-mapping>
--...
?</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
|
将
service-endpoint-interface-mapping 更改为使用原始的
population 接口。
<service-endpoint-interface-mapping>
?<service-endpoint-interface>
--
com.ibm.developerWorks.roundtrip.population
?</service-endpoint-interface>
?<wsdl-port-type?xmlns:seimwpt="http://roundtrip.developerWorks.ibm.com">
--seimwpt:population
?</wsdl-port-type>
?<wsdl-binding?xmlns:seimwb="http://roundtrip.developerWorks.ibm.com">
--seimwb:populationSoapBinding
?</wsdl-binding>
?<service-endpoint-method-mapping>
--...
?</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
|
| |
问题
| 该接口上的方法应该是原始的
GetALife ,而不是生成的
getALife 。
| |
改正
|
service-endpoint-interface-mapping 包含描述方法签名的
service-endpoint-method-mapping 构造。
?<service-endpoint-method-mapping>
--<java-method-name>
getALife</java-method-name>
--<wsdl-operation>GetALife</wsdl-operation>
--<wsdl-return-value-mapping>
--?<method-return-value>
----com.ibm.developerWorks.roundtrip.generated.Life
--?</method-return-value>
--?<wsdl-message?xmlns:wrvm="http://roundtrip.developerWorks.ibm.com">
----wrvm:GetALifeResponse
--?</wsdl-message>
--?<wsdl-message-part-name>GetALifeReturn</wsdl-message-part-name>
--</wsdl-return-value-mapping>
?</service-endpoint-method-mapping>
|
将
java-method-name 更改为原始的
GetALife。
?<service-endpoint-method-mapping>
--<java-method-name>
GetALife</java-method-name>
--<wsdl-operation>GetALife</wsdl-operation>
--<wsdl-return-value-mapping>
--?<method-return-value>
----com.ibm.developerWorks.roundtrip.life
--?</method-return-value>
--?<wsdl-message?xmlns:wrvm="http://roundtrip.developerWorks.ibm.com">
----wrvm:GetALifeResponse
--?</wsdl-message>
--?<wsdl-message-part-name>GetALifeReturn</wsdl-message-part-name>
--</wsdl-return-value-mapping>
?</service-endpoint-method-mapping>
|
| |
问题
| 我们需要将 Life Bean 中的数据作为公共变量公开。 | |
改正
|
variable-mapping 构造定义了 Bean 内的数据。在缺省情况下,该数据是作为 Bean 属性映射的(换句话说,它有 setters 和 getters)。
<java-xml-type-mapping>
?<java-type>com.ibm.developerWorks.roundtrip.generated.Life</java-type>
?<root-type-qname?xmlns:rtq="http://roundtrip.developerWorks.ibm.com">
--rtq:life
?</root-type-qname>
?<qname-scope>complexType</qname-scope>
?<variable-mapping>
--<java-variable-name>deathday</java-variable-name>
--<xml-element-name>deathday</xml-element-name>
?</variable-mapping>
?<variable-mapping>
--<java-variable-name>birthday</java-variable-name>
--<xml-element-name>birthday</xml-element-name>
?</variable-mapping>
</java-xml-type-mapping>
|
将
data-member 元素添加到
variable-mapping 会导致该数据作为公共变量进行映射。
<java-xml-type-mapping>
?<java-type>com.ibm.developerWorks.roundtrip.life</java-type>
?<root-type-qname?xmlns:rtq="http://roundtrip.developerWorks.ibm.com">
--rtq:life
?</root-type-qname>
?<qname-scope>complexType</qname-scope>
?<variable-mapping>
--<java-variable-name>deathday</java-variable-name>
--
<data-member/>
--<xml-element-name>deathday</xml-element-name>
?</variable-mapping>
?<variable-mapping>
--<java-variable-name>birthday</java-variable-name>
--
<data-member/>
--<xml-element-name>birthday</xml-element-name>
?</variable-mapping>
</java-xml-type-mapping>
|
|
 |
映射元数据文件版本
Web Services for J2EE已经经历了两个版本。上表中所示的映射元数据类型来自于 Version 1.1,它是 J2EE 1.4 的一部分。更早的版本与之略有不同。
|
|
映射元数据的完整格式信息可以在
Web Services for J2EE 规范
中找到。
映射元数据文件不是万能药
映射元数据文件可以解决许多往返问题。但是它并不包治百病。映射元数据文件对于解决一些 Java 编码惯例问题是有效的,而且对于解决类型映射中的一些多义性也是有效的。但是有许多问题无法通过映射元数据文件解决。
每个属性的不同类型
在我们的示例中,
birthday和
deathday都使用 Date 类。通过在映射元数据文件中更改日期的
全局映射,我们能够将它们更改为 Date 类。无法按照 Case-by-Case 的方式更改这些类型。因此,映射文件不能支持
Date birthday 和
Calendar deathday 。
这是一个普遍的问题,WebSphere 中的 WSDL2Java 工具有一种方法来处理它(请参阅下一部分)。
布尔型访问器
给定 Bean 属性,
property ,则 JavaBean 通常有
getProperty 和
setProperty 访问器。但是,如果
property 是布尔值,则该 Bean 必须有
isProperty 和
setProperty 方法。这不仅是一个 Java 编码惯例,而且是描述 JavaBean 的规则。如果不遵守该规则,Bean 就不是严格意义上的 JavaBean,并且 JAX-RPC Java-to-WSDL 工具可能不考虑此属性甚至整个 Bean。
WebSphere 中的 WSDL2Java 工具将识别布尔型
getProperty 方法,并且生成适当的 WSDL。但是 WSDL2Java 工具将生成布尔型
isProperty 方法。在映射元数据文件中没有辨别这两种样式的布尔型属性的机制,因为布尔型
getProperty 是一个不正确的访问器。
把
getProperty 当成一个布尔类型使用是不正确的,但是由于这佯做是如此的普遍,以致 WebSphere 中的 WSDL2Java 工具可以处理这些不同。
非 camelcase 访问器
给定 Bean 属性,
property ,则该 Bean 通常有
getProperty 和
setProperty 访问器。而有时 Beans 有
getproperty 和
setproperty 方法。同布尔型
getProperty 一样,非 camelcase 访问器是不合法的,并且不能作为访问器被识别。在将一个 Bean 映射到 WSDL 时,带有这些非法访问器的属性往往被忽略。
一种可能的万能药?
WebSphere Version 6.0 中的 WSDL2Java 工具具有一种功能,它可以不使用映射文件。它不是使用映射文件作为元数据来描述Web 服务,而是使用原始的类本身。它使用原始类的自身来代替映射元数据文件。通过这种方法,您可以使用原始的服务作为 WSDL2Java 的输入并与 WSDL 文件一起描述 Web 服务。
总结
通过映射元数据文件,可以解决头两个往返技巧中的往返问题。这将包括绝大部分往返问题。然而,还有一些往返问题不能通过映射元数据文件解决。许多这样的问题可以通过WebSphere WSDL2Java 工具提供的自省功能加以解决。
参考资料
作者简介  | |  | Russell Butek 是 IBM WebSphere Web 服务引擎的开发人员之一。他也是 JAX-RPC Java Specification Request(JSR) 专家组的 IBM 代表。他从事 Apache 的 AXIS SOAP 引擎的实现方面的研究,推动了 AXIS 1.0 遵循 JAX-RPC 1.0。以前,他是 IBM CORBA ORB 的开发人员和许多 OMG 特别工作组的 IBM 代表:包括可移植拦截器特别工作组(他是这个特别工作组的主席)、核心特别工作组以及互操作性特别工作组。 |
 | |  | Richard Scheuerle 是 IBM WebSphere Web 服务引擎的开发人员之一。他从事 Apache 的 AXIS SOAP 引擎的实现方面的研究。Richard 有 10 年的编译器/语言设计的开发经验。现在他侧重于 Web 服务引擎性能和 API 设计。Richard 也从事 CORBA 工具和芯片确认软件的开发。 |
对本文的评价
|