Java Web 服务: WSDL 1.1 理解与建模
2012-03-19 14:05:27 来源:WEB开发网
SOAP 1.1 vs. 1.2
SOAP 1.1 从 2000 年规范发布以来就一直广泛应用于 Web Services。SOAP 1.2 是由 W3C 基于更广泛行业支持而开发并在 2007 年作为一个 W3C 官方标准发布的。SOAP 1.2 比 SOAP 1.1 更整洁且文档更齐全,1.1 中一些不好的方面已经被无情地去除。尽管结构上更整洁,但是对于大多数 Web 服务 而言,这两个版本之间的实际差别很小。可能 SOAP 1.2 的最重要特性是它是惟一官方支持使用 XML-binary Optimized Packaging (XOP) 和 SOAP Message Transmission Optimization Mechanism (MTOM) 的 SOAP 附件增强特性的版本。到目前为止,我一直在 Java Web Services 系列文章中使用 SOAP 1.1,因为一些较老的协议并不支持 SOAP 1.2,但是 1.2 可能是一种更好的 Web 服务全新部署方法。
在
WSDL 服务描述的最后一个组件是
WSDL 处理
由于 WSDL 1.1 文档的模式和规则变化很多,所以很自然许多文档都不符合 WS-I BP 所规定的最佳形式。所有 Web 服务工具对许多最佳形式差异的支持助长了过时或错误结构的使用,从而导致行业中遍布许多不好的实践方法。而我也肯定免不了受到影响 — 检查我在本系列文章所提供的示例 WSDL 文档,我意外地发现没有一个代码是完全正确的。
所以当我准备撰写本篇文章时,我原本认为加入一个人们可用来验证 WSDL 文档是否符合最佳实践规则的工具是很不错的。只要原始 WSDL 文件没有错误,将 WSDL 文档转换成符合最佳实践方法的形式似乎是很简单的过程。但是结果证明这个过程比我最初设想的工作量要大得多,所以我将在本系列文章的后续两篇文章中详细介绍这个模型。
采用 Java 语言实现的 WSDL 文档处理模型有很多,包括广泛使用的 Web Services Description Language for Java Toolkit (WSDL4J),这是 JSR 110 的参考实现(见 参考资料)。但是似乎其中没有一个模型符合我真正希望实现的两个目标:第一,读取所有形式上不完全合理的 WSDL 文档,并报告错误及与最佳实践的差异;第二,重新编写符合最佳实践格式的正确 WSDL 文档。例如,WSDL4J 不会保持元素输入的顺序,这样我就会报告排序问题,也不会处理模式定义,所以它们不会直接用来检查
WSDL 模型
确认(Validation) vs. 验证(Verification)
我将在本文中使用验证 这个词来表示检查一个 WSDL 文档的正确性,因为它的同义词确认 通常在 XML 文档中用来表示根据模式定义检查文档的意思。
之前我已经将部分 WSDL 模型作为 JiBX/WS 项目的一部分来实现 JiBX 数据绑定。这个模型只是用作输出,并且它只包含相对较少的类,它们在某些情况下会包含 WSDL XML 结构内嵌元素的数据(包含一个
从 WSDL 1.1 的 WS-I BP 模式生成代码则是另一种方法。当我尝试这样做时,我认识到只使用所生成的类可能会很麻烦,因为这个模式包含了过多的类型,以及一些适合用来表现不同消息交换模式的结构(其有一些是后来 WS-I BP 规定中禁止使用的)。
所以我最终只是手动地创建这些类,虽然最终结果与我之前从模式生成代码,然后去掉不必要的重复内容并简化之后的结果是非常相似的。JiBX 数据绑定支持给相同的类提供多重绑定,所以我能够创建输入绑定来处理所有版本 WSDL 都支持的全部方法,同时配置输出绑定只输出最佳形式的 WSDL。
清单 3 显示了 Definitions 类中对应于根元素
清单 3. Definitions
class (部分)
public class Definitions extends ElementBase { /** Enumeration of child elements, in expected order. */ static enum AddState { invalid, imports, types, message, portType, binding, service }; /** List of allowed attribute names. */ public static final StringArray s_allowedAttributes = new StringArray(new String[] { "name", "targetNamespace" }); /** Validation context in use. */ private ValidationContextm_validationContext; /** Current state (used for checking order in which child elements are added). */ private AddState m_state; /** Name for this definitions. */ private String m_name; /** Target namespace for WSDL. */ private String m_targetNamespace; /** List of all import child elements. */ private List m_imports = new ArrayList (); /** List of all types child elements. */ private List m_types = new ArrayList (); /** List of all message child elements. */ private List m_messages = new ArrayList (); /** List of all portType child elements. */ private List m_portTypes = new ArrayList (); /** List of all binding child elements. */ private List m_bindings = new ArrayList (); /** List of all services child elements. */ private List m_services = new ArrayList (); /** Map from qualified name to message in this definition. */ private Map m_nameMessageMap = new HashMap (); /** Map from qualified name to port type in this definition. */ private Map m_namePortTypeMap = new HashMap (); /** Map from qualified name to message in this definition. */ private Map m_nameBindingMap = new HashMap (); /** Map from qualified name to service in this definition. */ private Map m_nameServiceMap = new HashMap (); ... /** * Check state transitions between different types of child elements. * If the elements are not in the expected order, * this flags the first out-of-order element for reporting. * @param state new add state * @param comp element component */ private void checkAdd(AddState state, ElementBase comp) { if (m_state != state) { if (m_state == null || (m_state != AddState.invalid && state.ordinal() > m_state.ordinal())) { // advanced on to another type of child element m_state = state; } else if (state.ordinal() < m_state.ordinal()) { // report child element out of order m_validationContext.addWarning ("Child element of wsdl:definitions out of order", comp); m_state = AddState.invalid; } } } ... /** * Add an unmarshalled wsdl:message child element. This also indexes the message by * name for validation access. * * @param child */ public void addMessage(Message child) { checkAdd(AddState.message, child); m_messages.add(child); addName(child.getName(), child, m_nameMessageMap); } ...
清单 3 中的子元素数据组织显示该模型是如何支持普通形式输入和最佳形式输出的。它不使用一个包含所有类型的子元素清单,而是为每一种类型创建不同的清单。 JiBX 绑定输入将子元素作为无序集处理,在每一个子元素乱序时调用该类元素特有的设置方法。设置方法会将这个实例添加一个有类型的清单,而不替换任何之前已有的值,您可以参考处理
所有的 WSDL 元素都允许添加扩展属性和元素(实际上是任何不使用 WSDL 1.1 名称空间的属性或元素)。本系列的前几篇文章的 WSDL 文档中所使用的 WS 策略配置就是一种这样的扩展元素,它们原来是实际的策略引用。这些扩展元素的最佳实践方法是它们用来引用 WSDL 1.1 名称空间中任意子元素的,并且这也是在输出绑定中对它们进行处理的方法。输入绑定会使用 WSDL 元素类的基类代码来处理扩展元素和属性,清单 3 没有包含这部分代码。而且它不关心元素的顺序(如果它们在 WSDL 1.1 名称空间的一个元素之后,那么就会出现一条警告)。
这个模型会为每一个扩展名称空间使用不同的绑定来处理已知的扩展元素,每一个扩展名称空间均对应不同集合的类。我将在下一篇 Java Web Services 文章对这些扩展元素进行更详细的介绍,同时提供更详细的源代码。
验证模型
WSDL 数据的一些基本验证是作为与 WSDL 文档树结构的元素对应的无序对象执行的,如 清单 3 末尾的 addMessage() 所示。这段代码使用 checkAdd() 方法来检查子元素的顺序,并使用 addName() 方法来保证提供了一个有效名称(与 NCName 模式类型相匹配,并且其值在该元素类型中是惟一的),并将这个名称映射到对象上。但是这只是单独地检查元素的最基本信息;我们还需要更多的验证代码来检查每一个元素的其他属性,以及元素之间的相互关系。
JiBX 允许您调用作为编组(marshalling)和解组(unmarshalling)过程一部分的用户扩展元素。WSDL 模型会使用一个这样的扩展元素,即一个后设方法,来运行这个验证逻辑。在相关对象的乱序完成之后它就会调用一个后设方法,所以执行对象验证检查通常是有用的。在验证 WSDL 时,最早的方法是是在一个后设方法之外执行根元素
更多的扩展
在本文中,您了解了 WSDL 的基本结构和用法,以及用于支持 WSDL 文档验证并将它们转换成最佳形式文档的 WSDL Java 数据模型。
本系列 的下一篇文章会进一步讨论这个话题,讨论编写 WS-Policy 和 WS-SecurityPolicy 断言时经常遇到的问题。它也会介绍 WSDL 模型和更深层次的验证过程,包括扩展这个模型以包含 WSDL 中嵌入的 WS-Policy/WS-SecurityPolicy 断言。
更多精彩
赞助商链接