使用 Rational Application Developer V7.0 开发 JAX-WS Web 服务

原文:http://www.ibm.com/developerworks/cn/rational/r-cn-radv7jax-ws/ 


JAX-WS 简介

JAX-WS 2.0 是继 JAX-RPC 1.1 之后的下一代 Web 服务标准,它提供了完整的 Web 服务协议栈,可显著减少开发和部署 Web 服务所需要的工作。JAX-WS 主要支持以下标准:

  • Java Architecture for XML Binding (JAXB) 2.0
  • Simple Object access protocol (SOAP) 1.2,以及 SOAP 1.1
  • Web Services Description Language (WSDL) 2.0,以及 WSDL 1.1
  • WS-I Basic Profile (BP) 1.1
  • SOAP with Attachments API for Java (SAAJ) 1.3

相对于 JAX-RPC,JAX-WS 拥有以下一些新特性:

  • 提供了用于将传统 Java 对象(Plain Old Java Object,POJO)类转换为 Web 服务的 Annotation 库,从而加速了 Web 服务的开发工作。
  • 提供了异步编程模型,支持对 Web 服务的异步调用。
  • 支持消息传输优化机制(Message Transmission Optimization Mechanism,MTOM),有效提高使用 SOAP 协议传输二进制格式附件的效率。

本文借助 RAD 集成开发平台,首先使用 Annotation 方式实现一个简单的 Web 服务端点,然后根据该服务端点的 WSDL 文件构建支持异步调用的 Web 服务客户机,最后在客户机与服务端点之间实现 MTOM 方式的二进制附件传输,并对此进行测试。

建立 RAD 开发环境

RAD 在 V7.0.0.3 以及后续版本中提供了对开发 JAX-WS Web 服务的支持。它包含了 Web Services Feature Pack(简称 WSFP)和 IBM WebSphere Application Server V6.1 Feature Pack for Web Services(简称 WASFP)这两个组件。WSFP 引入了一系列最新的 Web 服务标准,来支持 JAX-WS Web 服务的开发,这些标准包括 Web Services Reliable Messaging (WS-RM)、Web Services Addressing (WS-Addressing)、SOAP MTOM、JAXB 2.0、SOAP 1.2、SAAJ 1.3 等。WASFP 在 WebSphere Application Server V6.1 的基础上,提供了部署 JAX-WS Web 服务所需的运行时环境。

安装 WSFP 和 WASFP

要安装 WSFP 和 WASFP 这两个升级包,必须首先安装 RAD 的 V7.0.0.3 或更高版本(目前的最新版本是 7.0.0.5)的升级程序。

首次安装 RAD 时,可以通过在 IBM Installation Manager 的 Install Packages 界面上选择 Check for updates 来同时安装 V7.0.0.3 或更高版本的升级包,如图 1 所示。

图 1. 安装 V7.0.0.3 或更高版本的升级包

如果已经安装了 RAD,并且它的版本低于 V7.0.0.3,那么需要对 RAD 进行升级,以安装 WSFP 和 WASFP。选择 IBM Installation Manager 上的 Update Packages wizard 即可将 RAD 升级到 V7.0.0.3 或更高版本。

安装完 RAD 的 V7.0.0.3 升级包后,运行 IBM Installation Manager 上的 Modify Packages wizard,并在安装过程中的 Features 选项卡上选择 Web Services Feature Pack 和 IBM WebSphere Application Server V6.1 Feature Pack for Web Services 这两个组件,来完成安装,如图 2 所示。

图 2. 选择组件

配置 RAD 开发环境

在完成 WASFP 组件的安装之后,安装向导会自动创建一个使用该组件的名称为 AppSrvWSFP01 的 WebSphere Application Server 概要文件,因此需要在 RAD 中新建一个名为 WAS WSFP 的服务器定义来使用 AppSrvWSFP01 概要文件,该服务器定义将被用于测试 JAX-WS 应用程序。

至此,支持 JAX-WS 的 RAD 集成开发环境构建完毕。

使用 Annotation 开发 Web 服务端点

JAX-WS Annotation 是 Web Services Metadata for the Java Platform 规范(JSR-181)的一部分。使用 Annotation 可以方便地通过添加注释的方式来将一个 Java 类文件升级为 Web 服务,而不需要编写任何部署描述符,如 web.xml 或者 webservices.xml,也不需要手工创建 WSDL 文件。在 Web 服务部署期间,服务器将自动地生成或者更新这些描述符。

实现 Web 服务端点

本节将介绍如何在 RAD 中建立 JAX-WS Web 服务工程,并构建一个最简单的 Web 服务。

首先,依次点击 RAD 的“File”->“New”->“Dynamic Web Project”,创建一个名为“TestWS”的 Web 工程,同时为该 Web 工程新建一个名为“TestWSEAR”的 J2EE 工程,以方便测试。注意,在创建过程中的 Project Facets 页面,一定要选取 WebSphere 6.1 Feature Pack for Web Services 1.0。然后,再次创建一个名为“TestWSClient”的 Web 工程,同时将该 Web 工程关联到 TestWSEAR 工程中,以便后续创建 JAX-WS 客户机时使用。

在 TestWS 工程中添加一个 Java 类,代码如清单 1 所示。这段代码展示了提供 echoImage(byte[]) 函数的 JAX-WS Web 服务的最简单实现。该 Web 服务简单地将收到的字节数组回应到客户端。本文涉及到的所有源代码已经打包为 TestWSEAR.ear 文件,可从文章末尾给出的链接下载。

清单 1
// com.sample.service.Echo.java
package com.sample.service;
import javax.jws.WebService;
@WebService()
public class Echo {
public byte[] echo(byte[] bytes) {
 System.out.println("service done.");
 return bytes;
}
}

可以看到,与普通的 Java 类相比,该 Web 服务实现类仅仅是在类定义前增加了 @WebService 注释,它将类定义成为 Web 服务端点。在部署期间,服务器将在 TestWS 工程的 .apt_generated 文件夹中生成 Echo 和 EchoResonse 这两个辅助类,并根据 Annotation 自动更新相应的部署描述符文件。

此外,还可以在类定义前添加 @SOAPBinding 注释,来指定 Web 服务所绑定的 SOAP 消息协议类型;或是在方法定义前添加 @WebMethod 注释,来指定是否将该 Java 方法对外暴露为 Web 服务的某项操作,等等。这些 Annotation 可以很好的取代部署描述符的作用,对 Web 服务进行定义,大大简化了 Web 服务的开发工作。

在 JAX-WS 中,服务端点接口(SEI)将隐含于服务实现类中,因此这个服务类不需要额外实现任何接口;同时,JAX-WS 也不需要 jaxrpc-mapping 文件,因为它使用 JAXB 进行所有数据的绑定。

部署 Web 服务端点

在 RAD 的“Servers”面板上选中 WAS WSFP 服务器,点击右键选取“Start”启动该服务器;待服务器启动完成后,点击右键选取“Add and Remove Projects”,将 TestWSEAR 工程部署到服务器。等待部署完成,在 Web 浏览器中输入http://localhost:9081/TestWS/EchoService?wsdl,如果成功地部署了该服务,并且该服务正在运行,那么服务器会生成并返回该服务的 WSDL 文件。

在后续的章节中将介绍如何开发一个支持异步调用的客户端来对该 Web 服务进行进一步的测试。

开发支持异步调用的 Web 服务客户端

由于 Web 服务调用是通过网络来实现的,这种调用所花费的时间是无法预测的。交互式客户端或是某些对性能要求较高的客户端,由于必须等待 Web 服务的响应而严重地影响了它们的性能和用户体验。为了避免这种性能降低,JAX-WS 提供了新的支持异步调用的 Web 服务客户机 API(客户端代理)。借助该 API,应用程序开发人员不再需要自己创建线程,而是依赖 JAX-WS 异步客户机运行时为他们管理需要长时间运行的 Web 服务调用。当客户端程序使用 JAX-WS 客户机异步调用 Web 服务时,客户机会发送请求给 Web 服务端,并立即返回到客户端程序,这样客户端程序可以先继续处理后续的工作,而不用等待 Web 服务端返回结果。

生成 JAX-WS 客户端代理类

RAD 支持根据 JAX-WS Web 服务的 WSDL 文件自动生成客户端代理类。具体方法如下:在 Project Explorer 面板的 JAX-WS Web Services 文件夹,右键点击 TestWS:{http://test.jaxws.com/}EchoService,选择 Generate Client;然后在出现的 Web Serveices 面板(图 3)中选择 Server 为 WAS WSFP,选择 Web service runtime 为 IBM Websphere JAX-WS,选择 Client project 为 TestWSClient,最后勾选 Monitor the Web service,点击 Next 继续。

图 3. Web Serveices 面板


在出现的 Client Configuration 面板(图 4)中勾选 Enable asynchronous invocation for generated client 以生成支持异步调用的客户机,并在 Target package 输入 com.sample.stub,最后点击 Finish 生成代理类文件。下一节介绍的客户端程序将通过调用这些代理类来访问 Web 服务。

图 4. Client Configuration 面板

开发客户端程序

本节介绍的客户端程序以 HttpServlet 的方式,借助 RAD 生成的 Web 服务代理类,分别实现了对 EchoService Web 服务的同步调用方法 syncRequest()、轮询方式异步调用方法 pollRequest() 和回调方式异步调用方法 feedbackRequest();每个方法都发起了 2 次对 EchoService 的调用,并将服务返回的响应内容和方法执行时间显示在浏览器页面上。

同步调用

首先介绍 syncRequest() 同步调用方法的实现,见清单 2。在该方法中,依次对 EchoService 发起两次调用,每次调用时线程将阻塞等待 Web 服务返回结果,每次得到结果后就将返回值输出在浏览器页面上;最后输出该方法的总计执行时间。

清单 2
// com.sample.client.Client.java
private void syncRequest() throws IOException{
pw.println("Web service request 1 is sent, wait response...<br>");
pw.flush();
result = port.echo(req1.getBytes());
pw.write("<b>"+new String(result)+"</b>");
pw.flush();
wait(2000);
pw.println("Web service request 2 is sent, wait response...<br>");
pw.flush();
result = port.echo(req2.getBytes());
pw.write("<b>"+new String(result)+"</b>");
pw.flush();
}

可以通过链接 http://localhost:9081/TestWSClient/Client?type=sync 来测试该同步调用方法。运行结果如图 5 所示。由于对 Web 服务的调用是串行的,syncRequest() 方法的总计执行时间是两次 Web 服务响应时间之和。

图 5. 测试该同步调用方法
测试该同步调用方法

轮询模式异步调用

JAX-WS 提供了轮询模式和回调模式这两种异步调用模型。在轮询模型中,客户端程序发出调用,然后在对应的 Response 对象上不断轮询,以便在 Web 服务端给出响应后获取到调用的结果;Response 对象用于监视 Web 服务请求的状态,判断请求是否完成,并获取请求的结果。

此客户端程序以 pollRequest() 方法(清单 3)实现了轮询方式异步调用 EchoService。在该方法中也是依次对 EchoService 发起两次调用,但并不阻塞等待返回结果,而是在两次调用都发出之后,在各自的 Response 对象上调用 get() 方法来获取结果。调用 get() 方法时,如果 Web 服务的响应已经返回,那么将立即获取到返回值;否则,线程会在 get() 方法上一直等待到响应返回为止。

清单 3
// com.sample.client.Client.java
private void pollRequest() {
pw.println("Web service request 1 is sent, wait response...<br>");
pw.flush();
Response resp1=port.echoAsync(req1.getBytes());
wait(2000);
pw.println("Web service request 2 is sent, wait response...<br>");
pw.flush();
Response resp2=port.echoAsync(req2.getBytes());
try {
result=((EchoResponse)resp1.get()).getReturn();
pw.write("<b>"+new String(result)+"</b>");
pw.flush();
result=((EchoResponse)resp2.get()).getReturn();
pw.write("<b>"+new String(result)+"</b>");
pw.flush();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}

可以通过链接 http://localhost:9081/TestWSClient/Client?type=poll 来测试该轮询调用方法。运行结果如图 6 所示。从测试结果来看,采用轮询模式,客户端不用等待结果的返回,基本上实现了对 Web 服务的并发调用,显著提高了客户端程序的性能。

图 6. 运行结果
运行结果

回调模式异步调用

在回调模型中,JAX-WS 提供了 AsynchHandler 这个回调处理接口来接收并处理传入的 Response 对象。当响应到达,Response 对象被传入时,AsynchHandler 中的 handleResponse(Response) 方法将被执行,可以在 handleResponse(Response) 方法中编写相应代码从 Response 中获取请求结果。

如清单 4 所示,callbackRequest() 方法中也发起了两个异步 Web 服务调用,当调用结果返回时,handleResponse(Response) 方法将被执行,并通过调用 get() 方法获取到类型为 EchoResponse 的响应对象,最后调用 getReturn() 方法即可获取到返回值。

清单 4
// com.sample.client.Client.java
private void callbackRequest() throws IOException{
pw.println("Web service request 1 is sent, wait response...<br>");
pw.flush();
port.echoAsync(req1.getBytes(),this);
wait(2000);
pw.println("Web service request 2 is sent, wait response...<br>");
pw.flush();
port.echoAsync(req2.getBytes(),this);
}
public void handleResponse(Response resp) {
try {
result = ((EchoResponse)resp.get()).getReturn();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} 
}

可以通过链接 http://localhost:9081/TestWSClient/Client?type=callback 来测试该回调方法。运行结果如图 7 所示。回调模式和轮询模式的性能相近,开发人员可以根据实际情况合理选择。

图 7. 运行结果
运行结果

使用异步消息交换

默认情况下,上面提到的异步调用模式都没有实现消息级的异步,其本质均是借助 java.util.concurrent.Executor 类在编程模型上使用多线程的方式达到异步调用的目的。也就是说,Web 服务调用过程中的请求和响应并不是异步的。要实现消息级的异步调用,需要在客户端代理类的请求上下文中设置属性 com.ibm.websphere.webservices.use.async.mep 为 true,详细设置方法参见清单 5。

清单 5
//com.sample.stub.EchoService.java
 @WebEndpoint(name = "EchoPort")
 public Echo getEchoPort() {
 Echo echo=(Echo)super.getPort(new QName("http://service.sample.com/", 
                               "EchoPort"), Echo.class);
 Map<String, Object> rc = ((BindingProvider) echo).getRequestContext();
 rc.put("com.ibm.websphere.webservices.use.async.mep", Boolean.TRUE);
 return echo;
 }

设置该属性之后,客户端与服务器之间的消息交换将采用异步方式进行。Web 服务的请求消息中将包含一个 WS-Addressing 头,该头域提供了额外的路由信息,以帮助异步消息交换的顺利进行。这时,客户端会在某一端口监听,以等待响应的到来;客户端的地址和监听端口信息被记录到请求的 WS-Addressing 头域;当 Web 服务端收到请求后,会将响应发送到请求中 WS-Addressing 所记录的 ReplyTo 地址。客户端将一直监听直到响应到来,并不会因为超时而退出监听,但可以使用 Response.cancel() 方法(轮询模式)或 Future.cancel() 方法(回调模式)来停止监听。需要注意的是,在 Windows 平台下,ReplyTo 字段所记录的 URL 地址是以客户端的主机名为标识的,如果 Web 服务端不能解析该主机名,那么响应将不能到达客户端。为避免出现这个问题,可以在 Java 虚拟机中添加 -Dcom.ibm.websphere.webservices.transportEPRInIPAddr=yes 这个通用 JVM 参数,从而强制客户端在发送请求时以 IP 地址作为 ReplyTo 字段的标识。

为客户机和服务端点实现 MTOM

在 Web 服务的实现中,经常需要在 SOAP 报文中携带各种二进制格式的附件(如图像、文档等)一起传输。然而,SOAP 是一种基于 XML 的文本协议,只能使用 ASCII 组成的文本来表示数据,无法在报文中直接包含二进制格式的附件。

在 JAX-RPC 中使用 SOAP 发送二进制数据有两种方法,一种是把二进制数据通过 Base64 编码为 ASCII 格式,另一种是以 MIME 附件的方式发送。

SOAP 1.0 所支持的 Base64 编码方式简单易用,但是生成的 ASCII 文本大小相对于原始二进制格式有 30% 左右的增长。此外,对二进制文件进行 Base64 编解码将消耗大量的 CPU 资源。因此,这种方式只适用于发送比较小的二进制数据。

为了改进 Base64 编码的附件发送方式,SOAP 1.1 增加了对 SOAP Messages with Attachments (SwA) 规范的支持,SwA 将附件置于 SOAP 消息主体之外,基于 MIME 技术实现了 SOAP 消息同 SOAP 附件的封装。这在一定程度上解决了 Base64 编码所带来的性能问题,但这会导致大量的互操作性问题。

为了进一步改进二进制数据传输的性能和互操作性,JAX-WS 增加了对 MTOM 规范的支持。MTOM 是 W3C 所开发的一个标准,它通过 XOP(XML-binary Optimized Packaging)方式来实现二进制数据的传输。XOP 使用 MIME 将原始二进制数据引入到 XML 文档中,避免了 Base64 编码所带来的性能开销;同时借助 xop:Include 元素显式地将内容与附件关联起来,使得二进制数据的处理方式与文本数据的处理方式一致,避免了在 SwA 中存在的歧义性,较好地解决了互操作性问题。MTOM 可以优化二进制数据的传输,它会对具有 xs:base64Binary 标准格式字符组成的节点进行优化,能够显著提高附件传输的效率,是目前最具有优势的附件传输方式。

分别在客户端和服务端实现 MTOM

客户端的 sendImage(HttpServletResponse) 方法读取本地 BMP 位图并将其发送到 Web 服务端,然后等待以获取服务端响应,并将结果显示在浏览器页面上。

要在客户端实现 MTOM,只需要在客户端代理类的 SOAPBinding 上调用 setMTOMEnabled(true) 方法,以启用 MTOM,具体代码见清单 6。

清单 6
// com.sample.client.Client.java
private void sendImage(HttpServletResponse response) throws Exception {
 SOAPBinding binding = (SOAPBinding)((BindingProvider)port).getBinding();
 binding.setMTOMEnabled(true);
 URL url = getServletContext().getResource("/ibm.bmp");
File file = new File(url.getFile());
FileInputStream is = new FileInputStream(file);
long length = file.length();
byte[] buf = new byte[(int) length];
is.read(buf, 0, (int) length);
Object obj = port.echo(buf);
response.getOutputStream().write((byte[]) obj);
}

要在 Web 服务端点实现 MTOM,只需要在服务实现类上添加 @BindingType 注释即可,如清单 7 所示。

清单 7
// com.sample.service.Echo.java
package com.sample.service;
import javax.jws.WebService;
@WebService()
@javax.xml.ws.BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING)
public class Echo {
public byte[] echo(byte[] bytes) {
 return bytes;
}
}

使用 TCP/IP Monitor 测试 Web 服务

TCP/IP Monitor 是 RAD 中内置的一个监控 TCP 报文传输的工具,它在客户端和服务器之间充当请求 / 响应代理的角色,因此可以记录所有经过的 TCP 报文。利用该工具可以方便地监控 Web 服务的请求 / 响应 SOAP 消息,包括消息的内容、大小以及服务响应时间等等。

要启用 Monitor,需要打开 RAD 的 Preferences 设置(图 8),选择 TCP/IP Monitor,点击 Add 添加如图 9 所示的 Monitor 定义,并启动该 Monitor。

图 8. RAD 的 Preferences 设置


图 9. 启动该 Monitor


上述配置完成后,确保 TestWSEAR 应用已经正确发布到服务器并正在运行,然后在浏览器地址栏中输入http://localhost:9081/TestWSClient/Client,调用成功后,可以发现 Monitor 抓取到了请求和响应的 SOAP 报文。

图 10 是在启用 MTOM 前的测试结果:

图 10. 启用 MTOM 前的测试结果

图 11 是在启用 MTOM 后的结果,与上述结果相比较,可以看到在请求和响应的头域中多了“application/xop+xml”的类型设置,消息体变成了 MIME 格式,消息的字节数也有较大幅度的减少,如果传输大文件,效果将更加明显。

图 11. 启用 MTOM 后的结果


总结

JAX-WS 借助 Annotation 大幅度简化了 Web 服务的开发和部署,并且提供了支持异步调用的客户机编程模型和消息传输优化机制,使得 Web 服务的使用变得更为方便和灵活。IBM Rational Application Developer V7.0.0.3 提供了对开发、部署和测试 JAX-WS Web 服务的支持。本文说明了如何使用 RAD V7.0.0.3 构建 JAX-WS 客户端和服务端,并在这个过程中突出了 JAX-WS 的某些新特性。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值