内容


JAX-WS 中的错误和异常

Comments

引言

IBM® developerWorks 已经发布了多篇关于 Java API for XML-based RPC (JAX-RPC) 错误和异常的文章(请参见参考资料)。不过,现在出现了一个新的 Web 服务 Java 映射规范:JAX-WS。JAX-WS 映射与 JAX-RPC 映射有一些区别。本文将对这些新映射进行介绍。(JAX-RPC 和 JAX-WS 错误映射的比较不在本文讨论范围内。您可以通过查阅参考资料部分列出的其他 developerWorks JAX-RPC 文章自行进行比较。)

WSDL-to-Java 映射

首先,让我们从清单 1 中的 WSDL 入手,了解一下错误到异常的 WSDL-to-Java 映射。请使用您喜欢的 JAX-WS WSDL-to-Java 代码生成器处理此 WSDL。将会生成清单 2、3 和 4 中所示的接口和错误类。(所生成的代码要多得多,但我们这里仅讨论异常相关部分。)

清单 1. Faults.wsdl
<wsdl:definitions targetNamespace="urn:fault.sample"
    xmlns:tns="urn:fault.sample" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
    <xsd:schema targetNamespace="urn:fault.sample"
        xmlns:tns="urn:fault.sample" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="op" type="tns:Op"/>
      <xsd:complexType name="Op">
        <xsd:sequence>
          <xsd:element name="in" type="xsd:string"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="opResponse" type="tns:OpResponse"/>
      <xsd:complexType name="OpResponse">
        <xsd:sequence>
          <xsd:element name="out" type="xsd:string"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="faultElement" type="tns:Fault"/>
      <xsd:complexType name="Fault">
        <xsd:sequence>
          <xsd:element name="code" type="xsd:string"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="opRequest">
    <wsdl:part name="parameters" element="tns:op"/>
  </wsdl:message>
  <wsdl:message name="opResponse">
    <wsdl:part name="parameters" element="tns:opResponse"/>
  </wsdl:message>
  <wsdl:message name="faultMsg">
    <wsdl:part name="parameters" element="tns:faultElement"/>
  </wsdl:message>
  <wsdl:portType name="PT">
    <wsdl:operation name="op">
      <wsdl:input message="tns:opRequest"/>
      <wsdl:output message="tns:opResponse"/>
      <wsdl:fault name="fault" message="tns:faultMsg"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="Binding" type="tns:PT">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="op">
      <soap:operation soapAction=""/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="fault">
        <soap:fault use="literal" name="fault"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="Service">
    <wsdl:port name="Port" binding="tns:Binding">
      <soap:address location="http://www.example.org/"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

请注意,在清单 1 中,我刻意对每个错误组件以不同的方式进行命名,以便能了解 Java 类名称的来源。

  • Java 异常名称派生自 WSDL 错误消息的名称,在本例中为 FaultMsg
  • 所有映射的 JAX-WS 异常都包含私有 faultInfo 实例变量。此 faultInfo 的类型派生自错误的 wrapper 元素引用的模式,在本例中为 Fault

注意:为了简单起见,下列清单中已经从生成的 Java 代码删除了注释和标注。

清单 2. 生成的 Java 接口:PT.java
package sample.fault;

public interface PT {
    public String op(String in) throws FaultMsg;
}
清单 3. 生成的 Java 异常:FautMsg.java
package sample.fault;

public class FaultMsg extends Exception {
    private Fault faultInfo;
    public FaultMsg(String message, Fault faultInfo) {...}
    public FaultMsg(String message, Fault faultInfo, Throwable cause) {...}
    public Fault getFaultInfo() {...}
}
清单 4. 生成的 Java 错误信息类:Fault.java
package sample.fault;

public class Fault {
    protected String code;
    public String getCode() {...}
    public void setCode(String value) {...}
}

为什么错误的 JAX-WS 映射会创建错误类和错误信息类?JAX-RPC 不会这样。其中的原因在于,JAX-WS 将模式映射的生成委托给了 Java Architecture for XML Binding (JAXB)。JAX-WS 知道如何处理 Web 服务内容,但并不知道处理模式内容。JAXB 能识别模式。错误或异常是 Web 服务构件。错误中的数据是模式构件。因此,JAX-WS 生成器将生成异常,而 JAXB 生成器将生成包含异常的数据的 Java Bean。

Java-to-WSDL 映射

除了从异常的 Java Bean 数据生成的异常本身外,JAX-WS 似乎希望您按照其生成的方式构建异常。如果遵循此模式,将得到与前面部分描述相反的映射。

但是,将异常数据与异常分离的做法并不是 Java 程序员编写异常的典型方式。因此,JAX-WS 还提供了更为自然的异常映射。清单 5 和清单 6 显示了这个自然模式的一个简单示例。清单 7 显示了可以供 JAX-WS 生成器进行处理的 WSDL。

清单 5. 自然型 PT.java
package sample2.fault;

public interface PT {
  public String op(String in) throws Fault;
}
清单 6. 自然型 Fault.java
package sample2.fault;

public class Fault extends Exception {
    public Fault(String code) {...}
    public String getCode() {...}
    public void setCode(String value) {...}
}

异常到错误的 JAX-WS 映射实际上与 JAX-RPC 映射完全相同。

清单 7. 从上面的 Java 生成的 WSDL:PTImplService.wsdl
<definitions targetNamespace="http://fault.sample2/"
    xmlns:tns="http://fault.sample2/" xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
  <types>
    <xsd:schema>
      <xsd:import
          namespace="http://fault.sample2/"
          schemaLocation="PTImplService_schema1.xsd"/>
    </xsd:schema>
  </types>
  <message name="op">
    <part element="tns:op" name="parameters"/>
  </message>
  <message name="opResponse">
    <part element="tns:opResponse" name="parameters"/>
  </message>
  <message name="Fault">
    <part element="tns:Fault" name="fault"/>
  </message>
  <portType name="PTImpl">
    <operation name="op">
      <input message="tns:op"/>
      <output message="tns:opResponse"/>
      <fault message="tns:Fault" name="Fault"/>
    </operation>
  </portType>
  <binding name="PTImplPortBinding" type="tns:PTImpl">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="op">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
      <fault name="Fault">
        <soap:fault name="Fault" use="literal"/>
      </fault>
    </operation>
  </binding>
  <service name="PTImplService">
    <port binding="tns:PTImplPortBinding" name="PTImplPort">
      <soap:address location="https://localhost:9444/SampleFault/PTImplService"/>
    </port>
  </service>
</definitions>
清单 8. 从上面的 Java 生成的模式:PTImplService_schema1.xsd
<xs:schema targetNamespace="http://fault.sample2/"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://fault.sample2/">

  <xs:element name="Fault" type="tns:Fault"/>

  <xs:element name="op" type="tns:op"/>

  <xs:element name="opResponse" type="tns:opResponse"/>

  <xs:complexType name="op">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="opResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="Fault">
    <xs:sequence>
      <xs:element minOccurs="0" name="code" type="xs:string"/>
      <xs:element minOccurs="0" name="message" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

未建模错误如何处理?

当出现未建模错误时,会发生什么情况?例如,当上面的服务引发 sample2.fault.Fault 之外的异常(例如,NullPointerException)时,会发生什么情况?客户端会发生什么情况?其答案取决于消息传递协议。例如,通过 SOAP/HTTP 进行通信时,服务器端 SOAP 引擎会创建包含 SOAP 错误的 SOAP 消息(请参见清单 9),并在 faultcodefaultstring 字段提供与问题相关的信息。由于向客户端返回了 SOAP 错误,因此 JAX-WS 定义了名为 SOAPFaultException 的异常。当服务引发未建模错误时,客户端将收到 SOAPFaultException

javax.xml.ws.soap.SOAPFaultException 是特定于协议的异常。它对 javax.xml.ws.ProtocolException 进行了扩展。JAX-WS 定义了 ProtocolException 的另一个扩展:用于 XML/HTTP 通信通道的 javax.xml.ws.http.HTTPException。这些只是为 WSDL 定义的标准化绑定。对于其他绑定,假定绑定提供程序为每个新绑定分别定义了 ProtocolException 的其他扩展。

清单 9. SOAP 错误响应消息
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <soapenv:Fault>
      <faultcode>soapenv:Server</faultcode>
      <faultstring>java.lang.NullPointerException</faultstring>
      <detail/>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

其他异常

另外还有两个异常需要注意:WebServiceExceptionExecutionException

WebServiceException

javax.xml.ws.WebServiceException 并不是特别有意义。JAX-WS 定义了一系列 Java 类,如 ServiceBindingProviderDispatch。如果调用这些类时失败,将会引发 WebServiceException,即 JAX-WS API 的通用异常。

ExecutionException

java.util.concurrent.ExecutionException 不是 JAX-WS 异常(通过包名称就可以判断出来),它属于 Java Futures 模型的一部分。因为 JAX-WS 的异步客户端编程模型依赖于 Java Future 的模型,因此使用了此异常。在 Futures 模型中,异步调用引发的异常并不会直接引发。它们包装为 ExecutionException。当客户端尝试从异步调用获得响应时,将会引发此异常。清单 10 显示了异步调用清单 1 中定义的 Web 服务的代码片段。此代码片段进行调用(调用 proxy.opAsync),然后在异步调用完成之前进行其他操作。完成后,将获取响应。如果服务引发异常,则此代码将进入 catch 块。服务引发的实际异常在 ExecutionException 实例的 cause 字段中。(有关异步客户端编程模型的更多信息,请参见参考资料。)

清单 10. 异步异常代码片段
  try {
    PortProxy proxy = new PortProxy();
    Response<OpResponse> response = proxy.opAsync("NPE");
    while (!response.isDone()) {
      // do something
    }
    OpResponse opR = response.get(); // This call fails if there is a fault
    System.out.println("response = " + opR.getOut());
  } catch (ExecutionException ee) {
    ee.printStackTrace();
    Throwable t = ee.getCause();
    System.out.println("t = " + t.getClass().getName());
  }

总结

本文给出了 WSDL 错误到 Java 异常的一个实例 JAX-WS 映射。我们还了解了从 Java 异常到 WSDL 错误的映射。然后,我们对其他 JAX-WS 异常进行了讨论:

  • SOAPFaultException 及相关异常,客户端收到未建模错误时使用
  • WebServiceException,在 JAX-WS API 中使用
  • ExecutionException,在异步客户端程序中使用

相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=360003
ArticleTitle=JAX-WS 中的错误和异常
publish-date=12182008