WebSphere Application Server V6.1 Feature Pack for Web Services 扩展了 WebSphere Application Server V6.1 的功能,现在支持异步、可靠和安全地发送 Web 服务消息,并且以与其他供应商的互操作性为重点。Feature Pack 为新的 Web 服务标准提供支持,包括:
- Web Services Reliable Messaging (WS-RM)
- Web Services Addressing (WS-Addressing)
- SOAP Message Transmission Optimization Mechanism (MTOM)
- Web Services Secure Conversations (WS-SC)
它还支持以下基于标准的编程模型:
- Java API for XML Web Services (JAX-WS 2.0)
- Java Architecture for XML Binding (JAXB 2.0)
- SOAP with Attachments API for Java (SAAJ 1.3)
- Streaming API for XML (StAX 1.0)
在这个系列中,我们将关注 JAX-WS 2.0 客户端 API。
在 JAX-WS 中引入的新 Dispatch API 支持完全动态的服务调用。构建客户端不需要了解 WSDL 或 XML 文档,或者其他关于消息布局的知识。不过,这种灵活性要求开发人员必须非常熟悉 SOAP 协议和底层消息编写 API(比如 DOM 或 SAAJ)的细节。您可能需要使用 Dispatch 的场景的例子包括:
- 文档交换场景,在该场景中 Dispatch 的动态性非常重要。例如,实际消息是外部过程的输出,比如来自外部系统的文件或 XSLT 转换的结果。在这种场景中,使用 Dispatch 来提供 SOAP 信封,以及发送消息并返回响应而不需与实际的消息交互。
- 需要与更旧的、与 WS-I 不兼容的服务交互,比如与用 RPC 编码的服务交互。
- 需要使用 JAXB 之外的数据绑定技术。JAX-WS 构建在它与 JAXB 2.0 的紧密集成之上。尽管没有针对 JAX-WS 中的其他绑定的直接支持,但在撰写本文时,我们看到许多适合使用另一种数据绑定技术的场景,比如 Castor 或 XML bean。
- 未使用 SOAP 协议能够获得一些优势。Dispatch 支持 XML/HTTP 绑定,该绑定可用于调用基于应用程序定义的结构交换原始 XML 文档的端点。
本文提供:
- JAX-WS 概述
- 一个 JAX-WS Dispatch 客户端例子
- 几个用于处理 Web 服务消息的各种客户端 API 的例子
读者应该对 XML、SOAP、WSDL 和 Web 服务有良好的理解。
JAX-WS 2.0 是用于 Web 服务开发的新 Java API。它构建在旧的 JAX-RPC 编程模型之上,并且进行了许多改进。JAX-WS 2.0 的一些好处包括:
- 区分数据绑定技术和支持 Java Architecture for XML Binding (JAXB) 2.0 的 Web 服务编程模型。
- 支持 SOAP 1.2 和 SOAP 1.1。
- 通过支持 WS-I Basic Profile 1.1 改善 Web 服务的互操作性。
- 通过包含对在 JSR 181 中定义的注释(针对 Java 平台的 Web Services Metadata)和使用 JAX-WS 定义的注释的支持,添加没有描述器的 Web 服务部署。因为所有元数据都是标准化的注释,所以 Web 服务实现是独立于供应商的。
- 支持异步客户端编程模型。
- 简化处理程序的开发,并提供一个机制来允许处理程序与服务客户端和服务端点实现合作。
- 包含 Java Platform, Extended Edition (Java EE) 5 和 Java 2 Plaform, Standard Edition (J2SE) 6 中的 API。
图 1 显示了各种 JAX-WS 客户端 API 之间的类关系。JAX-WS 提供的这组 API 允许 Web 服务客户端调用部署在远程服务端点上的操作。您可以使用这些 API 以动态或静态、同步或异步的方式调用操作。
图 1. JAX-WS 客户端 API 的关系
本文的剩余部分描述如何从 WSDL 文档创建 Dispatch 客户端,然后解释所使用的 API。这个例子使用以下 WSDL:
清单 1. WSDL 定义
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="HelloWorldService"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.example.com/services/HelloWorld"
xmlns:types="http://www.example.com/schemas/HelloWorld"
targetNamespace="http://www.example.com/services/HelloWorld">
<types>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace=âhttp://www.example.com/schemas/HelloWorldâ>
<element name=âhelloâ>
<complexType>
<sequence>
<element name=âmessageâ type=âstringâ />
</sequence>
</complexType>
</element>
<element name=âhelloResponseâ>
<complexType>
<sequence>
<element name=âmessageâ type=âstringâ />
</sequence>
</complexType>
</element>
</schema>
</types>
<message name="HelloRequest">
<part name="firstName" element="types:hello"/>
</message>
<message name="HelloResponse">
<part name="greeting" element=âtypes:helloResponse"/>
</message>
<portType name="HelloWorldSEI">
<operation name="hello">
<input message="tns:HelloRequest"/>
<output message="tns:HelloResponse"/>
</operation>
</portType>
<binding name="HelloSoap11Binding" type="tns:HelloWorldSEI">
<soap:binding
style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="hello">
<soap:operation soapAction="hello"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="HelloService">
<port binding="tns:HelloSoap11Binding" name="HelloPort">
<soap:address
location="http://localhost:8080/hello/services/HelloService"/>
</port>
</service>
</definitions>
|
这个 WSDL 描述只有一个端口 HelloPort 的服务 HelloWorldService。portType HelloWorldSEI 定义一个 hello 操作。hello 操作使用封装的文档字面样式,因此发送 hello 请求时,将收到 helloResponse 响应。
让我们查看调用 hello 操作的简单 Dispatch 客户端。首先,我们查看整个调用,然后再查看细节部分:
清单 2. JAX-WS 客户端 javax.xml.ws.Dispatch 例子
// Qnames for service as defined in wsdl.
QName serviceName =
new Qname("http://www.example.com/services/HelloWorld", "HelloService");
//QName for Port As defined in wsdl.
QName portName =
new Qname("http://www.example.com/services/HelloWorld", "HelloPort");
//Endpoint Address
String endpointAddress =
"http://localhost:8080/hello/services/HelloService";
// Create a dynamic Service instance
Service service = Service.create(serviceName);
// Add a port to the Service
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);
//Create a dispatch instance
Dispatch<SOAPMessage> dispatch =
service.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE);
// Use Dispatch as BindingProvider
BindingProvider bp = (BindingProvider) dispatch;
// Optionally Configure RequestContext to send SOAPAction HTTP Header
Map<String, Object> rc = bp.getRequestContext();
rc.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
rc.put(BindingProvider.SOAPACTION_URI_PROPERTY, "hello");
// Obtain a preconfigured SAAJ MessageFactory
MessageFactory factory =
((SOAPBinding) bp.getBinding()).getMessageFactory();
// Create SOAPMessage Request
SOAPMessage request = factory.createMessage();
// Request Header
SOAPHeader header = request.getSOAPHeader();
// Request Body
SOAPBody body = request.getSOAPBody();
// Compose the soap:Body payload
QName payloadName =
new QName("http://www.example.com/schemas/HelloWorld", "hello", "ns1");
SOAPBodyElement payload = body.addBodyElement(payloadName);
SOAPElement message = payload.addChildElement("message");
message.addTextNode("Hello World!");
// Invoke the endpoint synchronously
SOAPMessage reply = null;
try {
//Invoke Endpoint Operation and read response
reply = dispatch.invoke(request);
} catch (WebServiceException wse){
wse.printStackTrace();
}
// process the reply
body = reply.getSOAPBody();
QName responseName =
new QName("http://www.example.com/schemas/HelloWorld", "helloResponse");
SOAPBodyElement bodyElement = body.getChildElements(responseName).next());
String message = bodyElement.getValue();
|
以上的例子通过下面的步骤使用 Dispatch API 调用一个端点上的操作:
- 创建和配置一个动态的
Service实例 - 创建 Dispatch 客户端
- 配置请求上下文
- 编写请求消息
- 调用操作
- 处理响应消息
下面的几个小节分别讨论这些步骤。
Service 类是一个表示 WSDL 服务的抽象。Service 实例可以是一个动态的服务(动态创建的服务)或一个静态的服务(由扩展服务类的 JAX-WS 工具生成的类)。在本文中,我将关注动态服务。
J2SE 客户端可以使用 Service.create() 方法来创建一个 Service 实例。当 J2SE 客户端选择创建一个 Service 实例时(如我们的例子所示),该实例就是一个动态 Service 实例,这表示它是动态配置的,而不是像生成的 Service Class 那样是静态配置的。可以通过两种方式创建动态的服务实例:
-
create(QName serviceName)为带有给定名称的服务返回一个服务对象。没有向该服务附加任何 WSDL 文档。如清单 2 所示,我们首先像在 WSDL 中那样定义
Service QName,然后使用它来创建一个Service。
清单 3. 创建动态的Service实例//Qnames for service QName serviceName = new Qname("http://www.example.com/services/HelloWorld", "HelloService"); //Create a dynamic Service instance Service service = Service.create(serviceName);
WSDL 服务是许多相关的端口的集合,每个端口都包含一个与特定协议绑定的端口类型,并且在特定的的端点地址上可用。如清单 2 所示,我们像 WSDL 描述的那样定义一个端口
QName,然后将该端口添加到Service。
清单 4. 将端口添加到ServiceQName portName = new Qname("http://www.example.com/services/HelloWorld", "HelloPort"); //Endpoint Address String endpointAddress = "http://localhost:8080/hello/services/HelloService"; // Add a port to the Service service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);
-
create(URL wsdlLocation, QName serviceName)为指定的 WSDL 文档返回服务对象并返回服务名,如清单 5 所示:
清单 5. 使用 WSDL 创建动态服务//WSDL URL URL wsdlLocation = new URL("http://www.example.com/services/HelloWorld?wsdl"); //Qname for service QName serviceName = new Qname("http://www.example.com/services/HelloWorld", "HelloService"); //Create a dynamic Service instance Service service = Service.create(wsdlLocation, serviceName);
我们也可以选择使用静态的服务。当使用 JAX-WS 工具编译 WSDL 文件来生成 Java 工件时,静态服务就变得可用。我们在这里选择动态服务的原因是:
- 我们不希望生成工件。
- 在编写客户端时我们不知道将要调用的服务。例如,我们可能从中介获得 WSDL 文档,然后使用它动态地配置服务对象。
- 我们仅使用 Dispatch 来发送在另一个系统中生成的消息。
Dispatch 是一个低级的 API,它需要客户端使用基于 XML 的技术构造消息或消息负载。更高级的 JAX-WS API 隐藏了处理 XML 的细节,并允许用户使用更加熟悉的 Java 对象。Dispatch 支持两种由常量标识的使用模式:
-
javax.xml.ws.Service.Mode.PAYLOAD -
javax.xml.ws.Service.Mode.MESSAGE
在 PAYLOAD 模式中,客户端应用程序仅处理
<soap:Body> 元素的内容(消息负载),而不是整个消息。如果服务使用 SOAP 绑定,JAX-WS 运行时负责提供 SOAP 信封。
清单 6. 在
PAYLOAD 模式下创建 Dispatch 实例//Create a dispatch instance Dispatch<SOAPMessage> dispatch = service.createDispatch(portName, javax.xml.transform.Source.class, Service.Mode.PAYLOAD); |
在 MESSAGE 模式下,客户端应用程序直接处理特定于协议的消息结构。客户端负责提供 SOAP 信封和消息负载。在我们的例子中,我们使用 MESSAGE 模式,如清单 7 所示。
清单 7. 在
MESSAGE 模式下创建 Dispatch 实例//Create a dispatch instance Dispatch<SOAPMessage> dispatch = service.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE); |
JAX-WS 客户端 API 实现 BindingProvider 接口,这允许它们在端点上为消息交换的请求和响应阶段维护独立的上下文。请求和响应上下文的类型为 Map<String, Object>, 并且使用 BindingProvider 接口的 getRequestContext 和 getResponseContext 方法获取。
下表显示了在创建了 Dispatch 实例之后可以在请求上下文上设置的标准属性:
| 属性 | 作用 |
|---|---|
| javax.xml.ws.service.endpoint.address | 作为特定于协议的 URI 的服务端点的地址。URI 的模式必须与正在使用的协议绑定匹配。 |
| javax.xml.ws.security.auth.usernamejavax.xml.ws.security.auth.password | 用于 HTTP 基本身份验证的用户名和密码。 |
| javax.xml.ws.session.maintain | 表示客户端希望加入 HTTP 会话。 |
| javax.xml.ws.soap.http.soapaction.usejavax.xml.ws.soap.http.soapaction.uri | 控制是否通过 HTTP 请求在 SOAP 中使用 SOAPAction HTTP 头部。 |
清单 8. 使用绑定提供者设置
RequestContext
//get Binding provider BindingProvider bp = (BindingProvider) dispatch; // Configure RequestContext (optional step) Map<String, Object> rc = bp.getRequestContext(); //Ask RequestContext to USE SOAPAction. rc.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE); //Add SOAPAction to RequestContext rc.put(BindingProvider.SOAPACTION_URI_PROPERTY, "hello"); |
我们的例子使用类型为 SOAPMessage 的 Dispatch,但 Dispatch 在编写消息方面还支持几种其他 API。例如,带有 DOMSource 的 javax.xml.transform.Source、SAXSource 和 StreamSource 子接口、JAXB 2.0 对象和 javax.activation.DataSource 都是有效的 Dispatch 类型。我们的例子通过使用 SAAJ SOAPMessage API 编写消息来演示调用,如清单 9 所示:
清单 9. 创建 SOAP 消息
//Create SOAPMessage Request SOAPMessage request = factory.createMessage(); //Request Header SOAPHeader header = request.getSOAPHeader(); //Request Body SOAPBody body = request.getSOAPBody(); |
从这里可以看到,payloadName 是 XML Schema “hello” 元素的 QName。
清单 10. 表示操作签名的 wsdl 元素
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com/schemas/HelloWorld">
<element name="hello">
<complexType>
<sequence>
<element name="message" type="string" />
</sequence>
</complexType>
</element>
</schema>
// Compose the soap:Body payload
QName payloadName =
new QName("http://www.example.com/schemas/HelloWorld", "hello", "ns1");
SOAPBodyElement payload = body.addBodyElement(payloadName);
SOAPElement message = payload.addChildElement("message");
message.addTextNode("Hello World!");
|
您可能很想知道为什么要这样构造消息。我们的简单的 WSDL 使用文档样式的 SOAP 绑定,如清单 11 所示:
清单 11. 文档样式的 SOAP 绑定
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> |
在这种样式中,我们发送 XML 文档。其他样式发送不同格式的消息。developerWorks 文章 Which style of WSDL should I use? 描述了不同的 WSDL 样式及其对应的消息。
在创建了请求之后,将使用 Dispatch 实例上的几个 Invoke 方法之一分发它。我们的例子使用同步请求-响应消息交换模式,如清单 12 所示:
清单 12. 分发带有请求的调用操作
try {
//Invoke Endpoint Operation and read response
reply = dispatch.invoke(request);
} catch (WebServiceException wse){
wse.printStackTrace();
}
|
如果未能成功分发请求,将抛出一个 WebServiceException,并且其原因是一个底层的异常(例如,HostNotFoundException)。如果端点在处理请求时出现问题,将抛出 SOAPFaultException。
从这个 Invoke 收到的响应最终被处理,即提取回复 SOAP 消息的主体并根据 WSDL 定义解析它。在这个例子中,我们希望接收到 helloResponse XML 元素,如清单 13 所示:
清单 13. 响应处理
<element name="helloResponse">
<complexType>
<sequence>
<element name="message" type="string" />
</sequence>
</complexType>
</element>
// process the reply
body = reply.getSOAPBody();
QName responseName =
new QName("http://www.example.com/schemas/HelloWorld", "helloResponse");
SOAPBodyElement bodyElement = body.getChildElements(responseName).next());
String message = bodyElement.getValue();
|
在本文中,我们学习了如何创建一个可用于动态地调用 Web 服务的 Dispatch 客户端。您还简单了解了其他一些 JAX-WS 客户端 API,它们可用于配置发送到服务器的 SOAP 消息的上下文。Dispatch API 的 WebSphere Application Server V6.1 Web Services Feature Pack 实现支持 javax.xml.transform.Source、JAXB Object、javax.xml.soap.SoapMessage 和 java.lang.String 类型的输入和输出消息类型。Web 服务客户端可以使用这个 Dispatch API 调用由 Web 服务端点实现的单向或双向的同步和异步操作。您可能需要使用 Dispatch API 调用 Web 服务的其他一些场景包括:
- 需要与遗留的 JAX-RPC 或不遵从 WS-I 的 Web 服务进行互操作时。
- 使用 xml/http 绑定而不是传统的 SOAP 绑定调用 Web 服务时。
- 使用数据绑定而不是 JAXB 调用 Web 服务时。
| 描述 | 名字 | 大小 | 下载方法 |
|---|---|---|---|
| 项目交换文件 | jaxwsDispatch.zip | 124KB | HTTP |
| 项目交换文件 | jaxwsProxy_samples.zip | 158KB | HTTP |
学习
-
WebSphere Application Server V6.1 的 Web Services Feature Pack 中的 JAX-WS 客户端 API,第 2 部分:创建代理客户端(developerWorks,2007 年):本系列的第二部分指导您创建代理客户端。
-
JSR 224: Java API for XML-Based Web Services (JAX-WS) 2.0:规范。
-
JSR 181: Web Services Metadata for the Java Platform:规范。
-
Web Services Description Language (WSDL) 1.1:WSDL 规范。
-
SOAP 1.2 Primer:SOAP 1.2 规范介绍。
-
The WS-I's profiles:着重描述 Basic Profile。
-
IBM developerWorks 中国:WebSphere 应用程序连接专区:关于 WebSphere Web 服务解决方案的技术资源,比如文章、教程和下载。
-
IBM developerWorks 中国:WebSphere Application Server:关于 WebSphere Application Server 的技术资源,比如文章、教程和下载。
-
IBM developerWorks 中国:WebSphere SOA:关于 WebSphere SOA 解决方案的技术资源,比如文章、教程和下载。
讨论
-
developerWorks Forum: Web Services Feature Pack on WebSphere Application Server V6.1:与其他开发人员一起讨论这个 Feature Pack。

