基于JAX-WS开发Web services
这篇文章纯属搬砖。
转载于:http://www.ithov.com/linux/125942_4.shtml。
Web Services 是一种面向服务的技术,通过标准的 Web 协议提供服务,目的是保证不同平台的应用服务可以互操作。依据 web services 规范实施的应用之间,无论它们所使用的语言、 平台或内部协议是什么,都可以相互交换数据,这就是 web services 的好处。本文选择 IBM WebSphere Application Server 作为 web services 的运行环境,并选择 IBM Rational Application Developer(以下简称 RAD)for WebSphere 就是作为本文的开发平台。RAD 针对 IBM WebSphere Application Server 的测试环境进行了优化,减少了开发人员因配置环境而消耗的时间。本文中展示的所有示例都是在 RAD for WebSphere 平台上开发、测试的。
Web Services 与 JAX-WS
Web Services 发展至今已有两种形式:REST 和 SOAP。REST Web Services 基于 HTTP 协议,SOAP Web Services 支持多种传输协议:HTTP、SMTP、MIME 等等。
本文主要介绍 SOAP web services。对于 JAVA,目前有两种 SOAP Web Services 规范:JAX-WS 和 SAAJ。
SOAP Web Services 通常要求服务器端提供一個机器可读的描述(通常基于 WSDL),以便客户端辨识服务器端提供的 Web 服务。
JAX-WS (Java API for XML Web Services) 是一组专门用于实现 XML Web Services 的 Java API。JDK 1.6 自带 JAX-WS 版本为 2.1。不过,JAX-WS 只提供 web services 的基础功能,所以如果你希望实现 web services 的复杂功能,比如 WS-Security,WS-Policy,WS-RM 等,那就需要切换到 Apache CXF 、Metro 或者 Axis。
本文的目标对象是初次接触 web services 或者 JAX-WS 的开发人员。所以本文将分享以下内容:
- Web Services 服务器端的开发
- Web Services 客户端的开发
- 基于 https 协议的 web services 通信
- 利用 @MTOM 优化网络大数据传输
JAX-WS web services 开发
RAD 是一个基于 Eclipse 的全功能集成开发环境(IDE),所以熟悉 Eclipse 的开发人员可以很快的上手使用 RAD 平台。
服务器端开发:
首先,在 RAD 平台上,创建一个最简单的 web service,这个 service 只向客户端返回一个字符串 – “Hello World”。服务器端的工作流程如下:完成 web services 编写,发布 web services 生成服务描述文件(WSDL),以供客户端获取。接下来,等待客户端发来的 SOAP 请求消息,解析其中的方法调用和参数格式。根据 WSDL 和 WSML 的描述,调用相应的对象来完成指定功能,并把返回值放入 SOAP 回应消息返回给用户。
首先在 RAD 中新建一个 Web Project – WebProjectDemo,
图 1. 创建 Web Project
选择相应的 Target Runtime(确认“Servers” view 中已创建 Server Runtime),并确保 “Add project to an EAR”是勾选上的,这样就不需要再手动创建 EAR 了。
图 2. 选择运行环境,完成 Project 创建
完成 Web Project 的创建。
接下来,开始编写 web service 类,开发方法很简单,只需用 @WebService 标注 Java 类为 web service 类,@WebMethod 标注类方法为 web service 方法。这些被标记的类和方法,在 service 发布之后,就能被客户端调用了。
清单 1. 服务端代码
@WebService public class HelloWorld { @WebMethod public String sayHello(){ return "Hello World!"; }
简短的五行代码就是 HelloWorld web service 类啦! HelloWorld 只有一个 web service 方法 – sayHello()。
接着就是发布 HelloWorld service 了,
图 3. 发布 HelloWorld Service
发布完成,在浏览器中敲入
http://WEBSERVER:9080/WebProjectDemo/HelloWorldService?wsdl,如果能看到以下界面,就表示发布成功了。接着,我们就开始 客户端 的开发。
图 4. WSDL 文件
客户端开发 :
本文选用 Eclipse 作用客户端的开发平台,因为 RAD 内置了 WAS JRE Runtime 库,易与 JAVA JRE Runtime 冲突,所以本文就换用 Eclipse 开发客户端。
客户端的工作流程如下:取得服务器端的服务描述文件 WSDL,解析文件内容,了解服务器端的服务信息以及调用方式(生成客户端 Stub)。编写客户端 SOAP 请求消息 ( 指定调用的方法以及调用的参数 ),发送给服务器端。等待服务器端返回的 SOAP 回应消息,解析得到的返回值。
有多种生成客户端 Stub 的方式可以选择,如:axis2、jax-ws、xfire 等,不过需要注意的是各开源软件对 soap 协议解析方式不同,所以生成的的客户端也不尽然相同。本文采用 jax-ws 生成客户端 Stub,步骤如下:
- 首先确认从客户端机器可以访问 HelloWorld Service:
- http://WEBSERVER:9080/WebProjectDemo/HelloWorldService?
- 在 Eclipse 中,新建 Java Project – WebServiceClient
- 打开命令控制台,运行命令: /jdk/bin/wsimport.exe -d C:\WebServiceClient\\bin -s C:\WebServiceClient\\src – keep http://WEBSERVER:9080/WebProjectDemo/HelloWorldService?wsdl
- 刷新 Project,会发现 src 文件夹下多了一些文件,见图 5。
图 5. 客户端文件结构 - 修改 HelloWorldService.java 。找到
wsdlLocation = "***.wsdl" 和 url = new URL(baseUrl, "***.wsdl") ,把 ***.wsdl 替换成http:// WEBSERVER:9080/WebProjectDemo/HelloWorldService?wsdl.
- 配置完成。现在我们就可以调用 Stub 与服务器端通信啦!新建 HelloWorldClient.java,
清单 2. 客户端代码public class HelloWorldClient { public static void main(String[] args) { HelloWorldService service = new HelloWorldService(); HelloWorld proxy = service.getHelloWorldPort(); System.out.println(proxy.sayHello()); } }
运行 HelloWorldClient.java,如果控制台输出“Hello World”,就表明客户端和服务器端通信成功了。
Https 与 web services
Web Services 采用网络传输数据,数据很容易暴露在外。在这种情况下,我们可以采用 Https 协议传输数据,建立一个信息安全通道,来保证数据传输的安全。当客户端与服务器端建立 https 连接时,客户端与服务器端需要经过一个握手的过程来完成身份鉴定,确保网络通信的安全。
对此,我们需要在客户端代码里做相应的处理:
- 利用浏览器导出 server 端证书,并保存为 demo.crt 。不同浏览器的导出步骤不一样,请查询相应的导出方法。
- 在客户端创建 truststore:/java/bin/keytool.exe -genkey -alias MYDOMAIN -keyalg RSA -keystore clienttruststore.jks
- 把 demo.crt 加入到 truststore 中: /java/bin/keytool.exe -import -trustcacerts -alias STOREALIAS -file demo.crt -keystore clienttruststore.jks
- 在客户端 HelloWorldClient.java 加入如下代码:
清单 3. Https 与 web services
public class HelloWorldClient { public static void main(String[] args) { System.setProperty ("javax.net.ssl.trustStore","PATH\\clienttruststore.jks"); //(truststore) System.setProperty ("javax.net.ssl.trustStorePassword", "password"); //(truststore 密码 ) System.setProperty ("javax.net.ssl.trustStoreType", "JKS"); //(truststore 类型 ) HelloWorldService service = new HelloWorldService(); HelloWorld proxy = service.getHelloWorldPort(); System.out.println(proxy.sayHello()); } }
身份鉴定完成,可以继续 web services 的探险了。
MTOM 与 web services
我们先来了解默认情况下 SOAP 是如何传输数据的:
在 SOAP 消息中所有的二进制数据都必须以编码之后的形态存在于 XML 文件中(为避免字符冲突)。正常文本 XML 使用 Base64 对二进制数据进行编码,这就要求每三个字节对应四个字符,从而使得数据的大小增加三分之一。如果我们需要传送 10M 的文件,编码之后文件大小就 13M。这种情况下,JAVA 引入了 MTOM(消息传输优化机制)消息编码。MTOM 就是针对 SOAP 消息传输的基础上提出的改进办法。对于大量数据的传递,不会进行 Base64 编码,而是直接以附件的二进制原始数据的形式封装在 SOAP 消息的 MIME 部分,进行传输。使用 MTOM 的目的在于优化对较大的二进制负载的传输。对于较小的二进制负载来说,使用 MTOM 发送 SOAP 消息会产生显著的开销,但是,当这些负载增大到几千个字节时,该开销会变得微不足道。
现在,我们利用 MTOM 机制实现一个提供上传和下载文件功能的 HelloWorld Web Service。首先,在服务器端增加 upload() 和 download() 两个方法,并通过添加标注 @MTOM,在服务器端开启 MTOM 消息传输功能,同时选用 datahandler 类型封装传输文件。并使用 @XmlMimeType("application/octet-stream") 标注 datahandler,以表示这是一个附件类型的二进制数据。
清单 4. MTOM 服务端代码
@MTOM @WebService public class HelloWorld { private static final String REPOS = "/home/webserviceTest"; @WebMethod public String sayHello(){ return "Hello World!"; } /** * 上传文件到 Server,并命名为 @fileName */ @WebMethod public String upload( @XmlMimeType("application/octet-stream") DataHandler handler , String fileName){ try { File file = new File (REPOS + "/" + fileName); OutputStream output = new BufferedOutputStream( new FileOutputStream(file)); handler.writeTo(output); output.close(); } catch (IOException e) { e.printStackTrace(); } return "Success"; } /** * 从 Server 下载名为 @fileName 的文件 */ @WebMethod public @XmlMimeType("application/octet-stream")DataHandler download( String fileName){ DataHandler dh = new DataHandler (new FileDataSource(REPOS + "/" + fileName)); return dh; } }
每次重新发布 web service 之后,都需要在客户端重新运行 wsimport,保持客户端 Stub 与 web service 一致。现在,我们就在客户端调用 HelloWorld 的 upload() 方法上传 test.txt 文件到服务器,并通过 download() 从 Server 下载名为 wsDemo 文件。
客户端开启 MTOM 的方式和 Server 端不同,请参见客户端代码。
清单 5. MTOM 客户端代码
HelloWorldService service = new HelloWorldService(); HelloWorld proxy = service.getHelloWorldPort(new MTOMFeature()); // 开启 MTOM File file = new File(test.txt"); DataHandler dataHandler = null; dataHandler = new DataHandler(new FileDataSource(file)); try { // 上传 test.txt 文件到 Server,并命名为 wsDemo System.out.println("Hello World Upload Function : " + proxy.upload(dataHandler,"wsDemo")); } catch (SOAPFaultException ex) { System.out.println(ex.getMessage()); } // 下载文件 wsDemo,并保存到本地。 DataHandler data = proxy.download("wsDemo"); File serverFile = new File(".\\data\\server_test.txt"); data.writeTo(new FileOutputStream(serverFile));
我们利用 upload() 方法上传 5 个不同大小的文件 (1M,10M,50M,100M,300M),并统计上传这些文件分别消耗的时间:
图 6. Upload() 消耗时间统计
图 7. Upload() 消耗时间线性图
可以看出,随着文件大小的增长,上传文件所消耗的时间几乎是呈线性增长的。并且实际消耗时间和预期消耗时间(以上传 1M 文件的时间为基准,线性计算)的相差值也是在可以接受的范围内。这完全符合我们的预期期望。
结束语
本文展示利用 JAVA Web Services 规范 JAX-WS 实现 web services 客户端和服务器端通信。SOAP Web Services 使用 HTTP 传送 XML,不过由于 HTTP 的限制以及需要额外的消耗解析 XML 文件,使得 SOAP 的通信速度低于其它方案。但另一方面,XML 是一个开放、健全、有语义的讯息机制,而 HTTP 是一个广泛又能避免许多关于防火墙的问题,从而使 SOAP 得到了广泛的应用。所以,如果效率对项目是一项很重要的指标的话,就需要慎重考虑是否使用 SOAP 实现 Web Services。