WEB开发网
开发学院软件开发Java Java Web 服务: WSDL 1.1 理解与建模 阅读

Java Web 服务: WSDL 1.1 理解与建模

 2012-03-19 14:05:27 来源:WEB开发网   
核心提示: SOAP 1.1 vs. 1.2SOAP 1.1 从 2000 年规范发布以来就一直广泛应用于 Web Services,SOAP 1.2 是由 W3C 基于更广泛行业支持而开发并在 2007 年作为一个 W3C 官方标准发布的,Java Web 服务: WSDL 1.1 理解与建模(3),SOAP 1.2 比

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 服务全新部署方法。

元素表示 所定义抽象接口的一个实例,见 清单 2 所示的 BookServerImpl.wsdl 的开头部分。type 属性指定了这个绑定所实现的端口类型全名。

的子元素说明规定了这个端口类型是如何实现的。WSDL 名称空间的子元素对应于 所指定端口类型,并且必须使用相同的 name 属性值 — 和 引用一样不是 完整名称空间引用。图 1 在 上使用虚线表示这个连接。相同的名称连接也用在 的子元素 // 上。尽管重用了相同的元素名称,但如果这些元素是 元素的子元素而不是 元素的子元素,那么它的内容是差别很大的。

是 WSDL 所定义的扩展发挥作用的地方。 子元素将用于定义一个 SOAP 服务(这是 WS-I BP 所支持的惟一服务类型,尽管 WSDL 1.1 也支持 HTTP 绑定)。这个 元素会使用必需的 transport 属性来定义该绑定所使用的传输类型。(如 清单 2 中 http://schemas.xmlsoap.org/soap/http 的值所示,HTTP 是 WS-I BP 所支持的惟一选择。)您可以使用可选的 style 属性来选择 RPC 或文档类型作为 XML 数据表现(默认最常用的是 document,它通过模式元素定义与消息保持一致,而不是类型定义)。

的每一个 子元素中,必须使用一个 元素指定一个 SOAPAction 值,用来确定调用这个操作的请求(也可能用于重写 元素所指定的 RPC 或文档类型,但是 WS-I 禁止这个用法)。每一个 // 子元素都包含另一个扩展元素,在 清单 2 所示情况中, 中总是 (表示消息数据中在 SOAP 消息主体中发送的 — 在 SOAP 头中也可能发送数据,甚至错误,但是我认为这种不是一种好的做法),而在 则使用等价的

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 文档子集,但是当我开始考虑基于该模型开发一个验证和重建工具时,我认识到支持可能设计不当的 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 ValidationContext m_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 绑定输入将子元素作为无序集处理,在每一个子元素乱序时调用该类元素特有的设置方法。设置方法会将这个实例添加一个有类型的清单,而不替换任何之前已有的值,您可以参考处理 子元素所使用的 addMessage() 设置方法。在获取元素可能不是预期的顺序,所以每一个设置方法也会在每一次获取时检查元素状态。

所有的 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 文档验证并将它们转换成最佳形式文档的 WSDL Java 数据模型。

本系列 的下一篇文章会进一步讨论这个话题,讨论编写 WS-Policy 和 WS-SecurityPolicy 断言时经常遇到的问题。它也会介绍 WSDL 模型和更深层次的验证过程,包括扩展这个模型以包含 WSDL 中嵌入的 WS-Policy/WS-SecurityPolicy 断言。

上一页  1 2 3 

Tags:Java Web 服务

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接