JAX-WS 2.0 的全称为 Java API for XML-Based Web services (JAX-WS) 2.0。JAX-WS 2.0 是对 JAX-RPC 1.0 规范的扩展,是 JAX-RPC 1.1 的后续版本, JAX-RPC 2.0 标准发布不久后便被重新命名为 JAX-WS 2.0。 JAX-WS 2.0 是面向 Java 5 的开发 Web services 的最新编程标准,它提供了新的编程模型和对以往的 JAX-RPC 方式的 Web services 进行了增强。在 WebSphere 应用服务器 6.1 Web services 功能部件包中提供了对 JAX-WS 2.0 的支持。本文结合 WebSphere 里的开发工具 AST(Application Server Toolkit) 讲述了 JAX-WS 2.0 标准下 Web services 的开发过程,同时介绍了 JAX-WS 2.0 一些重要的 API 和命令行工具 wsimport,wsgen 的使用。本文对于想了解 Web services 最新变化和准备采用 JAX-WS 2.0 编程的相关人员有一定的帮助。
|
|
和以往的 JAX-RPC 相比,JAX-WS 2.0 的主要特点有:
此外,功能部件包同时提供了对其它如 JAXB_2.0, MTOM/XOP API 以及基于 PolicySet 安全化 Web services 等高级特性的支持。
|
|
同 基于 JAX-RPC 的 Web services 一样,JAX-WS 2.0 的开发分为 service 端和 client 端。在 service 端,可以选择自顶向下或自底向上的方式来创建 Web services,这两种方式可以通过 AST 或命令行工具来完成。在 client 端,一般通过 AST 或命令行工具根据 WSDL 文件创建用于调用 Web services 的辅助类,然后再在自己的代码中使用这些辅助类。 JAX-WS 2.0 提供了多种 client 端的编程模型。下面将分别介绍 service 端和 client 端的开发过程。
JAX-WS 2.0 有两种开发过程 : 自顶向下和自底向上。自顶向下方式指通过一个 WSDL 文件来创建 Web services,自底向上是从 Java 类出发创建 Web services。两种开发过程最终形成的文件包括:
Web services 既可以包含在一个 web module 中发布,也可以包含在 EJB module 中发布。下面我们结合实例来看一个自顶向下的 Web services 的开发过程。该例子包含的 Web services 很简单:一个 service,该 service 只有一个 port,该 port 只有一个操作 addNumber。
首先,我们开发一个普通的 Java Bean。在 AST 中创建两个项目:一个动态 web 项目,包含我们示例的 Web services;另一个 J2EE 项目,用于生成 ear。
清单 1. 用于创建 Web service 的一个普通 Java Bean
package com.ibm.ws.demo.server; public class SimpleBean { public int addNumber(int i , int j) { return i + j; } } |
在 AST 工具中,选中该类,在右键弹出菜单中,启动 Web service 创建向导。在向导中,您需要选择 Web services 运行时的 runtime 为 IBM WebSphere JAX-WS。同时,选择生成 WSDL 文件。其余步骤按缺省设置。
最后生成的项目结构如下:
上 图中,SimpleBeanDelegate.java, AddNumber.java 和 AddNumberResponse.java 为向导生成的 java 类,WebContent 下还生成了两个 Web service 的描述文件 SimpleBeanService.wsdl 和 SimpleBeanService_schema1.xsd。
在 AST 中导出 ear 文件,在 WebSphere 管理控制台上部署,再启动应用。在浏览器中通过下面形式的 URL 可以验证 Web services 是否成功启动。
http://hostname:port/webmodule/QName
注意 AST 中自动生成的一个代理类 SimpleBeanDelegate.java:
清单 2. 用于部署 Web service 的代理类 SimpleBeanDelegate.java
import javax.jws.WebService; import javax.xml.ws.BindingType; import javax.jws.soap.SOAPBinding; @WebService (targetNamespace="http://server.demo.ws.ibm.com/", serviceName="SimpleBeanService", portName="SimpleBeanPort", WSDLLocation="WEB-INF/WSDL/SimpleBeanService.WSDL") public class SimpleBeanDelegate{ SimpleBean _simpleBean = new SimpleBean(); public int addNumber(int i, int j) { return _simpleBean.addNumber(i,j); } } |
上面这个类是真正用于部署 Web services 的类,该类采用了 Annotation 来描述 Web services。 Annotation 是 Java 5 的一大语言特性,采用 Annotation 描述 Web services 的元数据,减轻了开发者的部署负担。在 javax.jws 包中定义了如下主要的 Annotation:
客 户端开发的通常过程是从已有的 WSDL 出发,创建辅助类 JAXB 对象和 service 代理类,然后基于这些类开发自己的客户端应用。在 AST 中,选中工程 SimpleJaxWS 下的 WSDL 文件 SimpleBeanService.WSDL,在右键菜单中启动 Web services 客户端创建向导。在创建过程中,同样需要选择 runtime 为 IBM WebSphere JAX-WS, 其它按默认设置。最终生成的客户端项目结构如下:
其 中自动生成的 Java source 包括 JAXB 对象 , 如上图中的 AddNumber.java,AddNumberResponse.java, ObjectFactory.java 以及 package-info.java 类,和 service 代理类,如上图中的 SimpleBeanDelegate.java, SimpleBeanPortProxy.java 以及 SimpleBeanService.java 类。然后,再写一个测试类 WSClient.java,该类是一个独立运行的 Java application。测试时,您需要在 classpath 中添加一个 thin client:com.ibm.jaxws.thinclient_6.1.0.jar。该 jar 在 $WAS_HOME/runtimes 目录下可以找到。
清单 3. 调用 Web service 的客户端 WSClient.java
public class WSClient { QName serviceName = new QName("http://server.demo.ws.ibm.com/", "SimpleBeanService"); QName portName = new QName("http://server.demo.ws.ibm.com/", "SimpleBeanPort"); String host = "9.186.117.142" ; String port = "9080" ; String endpt ; String WSDLURL ; SimpleBeanService service = null; public WSClient() { endpt = "http://"+ host +":" + port +"/SimpleJaxWS/SimpleBeanService" ; WSDLURL = "http://"+ host +":" + port + "/SimpleJaxWS/SimpleBeanService/WEB-INF/WSDL/SimpleBeanService.WSDL" ; |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error: The previous line is longer than the max of 90 characters ---------| } /** * Get the SEI from endpoint address * @param endpt * @return */ public SimpleBeanDelegate getSEIFromEndpoint() { SimpleBeanDelegate port = null ; BindingProvider p = null; try { service = new SimpleBeanService(null, serviceName); System.out.println("Looking up service " + service.getServiceName()); port = service.getSimpleBeanPort(); p = (BindingProvider) port; p.getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpt); //p.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE); |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error: The previous line is longer than the max of 90 characters ---------| //p.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, "invoke"); |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error: The previous line is longer than the max of 90 characters ---------| } catch (Exception e) { e.printStackTrace() ; } return port ; } public void simpleCall() { try { SimpleBeanDelegate port = getSEIFromEndpoint() ; System.out.println(port.addNumber(8, 9)); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { WSClient client = new WSClient() ; client.simpleCall() ; } |
客户端调用 Web service 的常见过程为:先创建一个 service 对象,再从 service 对象获得 SEI,最后调用 SEI 的方法去访问 Web service。
除 了上述的同步调用方式外,JAX-WS 2.0 还提供了异步调用,采用异步调用,客户端不需要等待 SOAP response 消息的返回,就可以继续别的处理。异步调用方式有 polling 和 callback 两种,这里只讲 callback 方式。 callback 就是提供一个 callback 函数,供消息返回时调用。在 Java 中,该 callback 函数是一个实现了特定接口的类。如果要异步方式访问 Web services,首先得在 SEI 中生成一个异步调用的方法。在用 AST 中的 Web services 向导工具创建 Web services 客户端时,注意选中“生成异步调用方法”:
生成的异步调用方法如下:
清单 4. 用于异步调用 Web service 的方法定义
@WebMethod(operationName = "addNumber") @RequestWrapper(localName = "addNumber", targetNamespace = "http://server.demo.ws.ibm.com/", className = "com.ibm.ws.demo.client.AddNumber") @ResponseWrapper(localName = "addNumberResponse", targetNamespace = "http://server.demo.ws.ibm.com/", className = "com.ibm.ws.demo.client.AddNumberResponse") public Future < ? > addNumberAsync( @WebParam(name = "arg0", targetNamespace = "") int arg0, @WebParam(name = "arg1", targetNamespace = "") int arg1, @WebParam(name = "asyncHandler", targetNamespace = "") AsyncHandler < AddNumberResponse > asyncHandler); |
当 SOAP response 返回时,客户端 runtime 会调用我们提供的 AsyncHandler。下面是一个实现了 AsyncHandler 的类。
清单 5. 用于异步调用 Web service 的 Handler 类,处理返回的 SOAP 消息
import javax.xml.ws.AsyncHandler; import javax.xml.ws.Response; public class CallbackHandler implements AsyncHandler <AddNumberResponse>{ public void handleResponse(Response response) { AddNumberResponse res = (AddNumberResponse)response ; //do something with the response... System.out.println("Response result is : " + res.getReturn()); } } |
最终,客户端异步调用 Web services 的形式为:
清单 6. 异步调用 Web service 的代码
public void asyncCall() { try { SimpleBeanDelegate port = getFromJavaBeanImpl(endpt) ; CallbackHandler asyncHandler = new CallbackHandler() ; port.addNumberAsync(8, 9, asyncHandler) ; //do other things... } catch (Exception e) { e.printStackTrace(); } } |
JAX-WS 2.0 还提供了 service 端和 client 端的动态编程模型:Provider/Dispatch 模型。Provider<T> 和 Dispatch<T> 分别是 service 端和 client 端提供的两个接口,该编程模型可以让您在 xml message 级别上工作。换言之,您可以通过编程操纵整个 SOAP 信封或 SOAP 信封的 body。 T 的类型和另一个参数 Service.Mode 组合在一起,决定了操作的 xml message 到底是整个 SOAP 信封还是部分。T 的两种类型为 Source 和 SOAPMessage,两种 Service 模式为 Service.Mode.MESSAGE 和 Service.Mode.PAYLOAD。下面给出了其中一种组合的示例。
Service 端的代码示例
清单 7. 以 Provider 模式实现 Web service 的 service 端 Java 类
@WebServiceProvider @ServiceMode(value=Service.Mode.MESSAGE) public class ProviderImpl implements Provider<SOAPMessage> { public SOAPMessage invoke(SOAPMessage msg) { //here the msg is the entire SOAP request // do request processing SOAPMessage response = ...; return response; } } |
Client 端的代码示例
清单 8. 以 Dispatch 模式调用 Web service 的 client 端 Java 类
//create the service Service service = Service.createService(QName serviceQName); //or Service service = Service.createService(URL WSDLLocation, QName serviceQName); //set the binding service.addPort(QName portName, String SOAPBinding.SOAP11HTTP_BINDING, String endpointAddress); //create the dispatch Dispatch dispatch = service.createDispatch(QName portName, Class clazz, Service.Mode mode); //Dispatch dispatch = service.createDispatch(QName portName, JAXBContext jaxbcontext, Service.Mode mode); //invoke the Web services Object response = dispatch.invoke(T); dispatch.invokeOneway(T); //or Response<T> response = dispatch.invokeAsync(T); //or Future<?> response = dispatch.invokeAsync(T, AsyncHandler); |
这里的问题是如何生成类型 T 的实例,答案是通过 JAXB 标准提供的 API 将 Java object 转化为 xml message 对象。
|
|
通过 wsgen,wsimport 工具创建 Web services
除 了 AST 中提供的图形界面开发方式外,Web services 功能部件包还提供了创建 Web services 的命令行工具。其中,wsgen 支持从 Java class 创建 Web services,wsimport 支持从 WSDL 创建 Web services。分别对应于 JAX-RPC 方式下的 Java2WSDL 和 WSDL2Java。
清单 9. wsgen 命令格式
wsgen [options] < SEI >
主要选项:
注 意:这里的 SEI 是一个 endpoint implementation class,而不是一个接口。你必须先写好一个 endpoint 的实现类,如上面的 SimpleBeanDelegate,该类中用 @WebService 声明好 Web service,再将它编译成 class 文件,才能提供给 wsgen 使用。
清单 10. wsgen 使用实例
wsgen.sh -cp classes -s src -d bin -r web -WSDL com.ibm.ws.demo.server.SimpleBeanDelegate |
清单 11. wsimport 命令格式
wsimport [options] <WSDL_URI>
主要选项:
清单 12. wsimport 使用实例
wsimport.sh -d classes -s src -WSDLlocation /WEB-INF/WSDL/SimpleBeanService.WSDL SimpleBeanService.WSDL |
|
|
通过 Ant task 自动构建 Web services 应用
WebSphere 同时提供了用于创建 Web services 的 ant task,这些 task 的参数和 wsgen,wsimport 是一致的。利用这些 ant task,您可以在大型项目中完成自动构建工作。
清单 13. 在 Ant 中使用 wsgen 和 wsimport
<wsgen sei="..." destdir="directory for generated class files" classpath="classpath" resourcedestdir="directory for generated resource files such as WSDLs" sourcedestdir="directory for generated source files" keep="true|false" verbose="true|false" genWSDL="true|false" protocol="soap1.1|soap1.2" servicename="..." portname="..." extension="true|false"> <classpath refid="..."/> </wsgen> <wsimport WSDL="..." destdir="directory for generated class files" sourcedestdir="directory for generated source files" keep="true|false" extension="true|false" verbose="true|false" version="true|false" WSDLLocation="..." catalog="catalog file" package="package name"> <binding dir="..." includes="..." /> </wsimport> |
结束语
本 文向您讲述了 JAX-WS 2.0 的主要特性,并通过示例展示了 Web services 新的编程模型和 API 及其开发过程。通过本文,您可以对 JAX-WS 2.0 有一个初步的了解,并懂得如何通过 WebSphere 提供的工具开发简单的符合 JAX-WS 2.0 的 Web services。值得说明的是,本文并没有向您介绍 JAX-WS 2.0 的全部特性,您可以通过本文提供的参考资源获得更多信息。
学习
获得产品和技术
讨论
参与 developerWorks Blog ,从而加入到 developerWorks 社区中来,其中包括以下与 SOA 和 Web 服务相关的 Blogs: