1. WebService简介
1. Web Services是由企业发布的完成其特定商务需求的在线应用服务,其他公司或应用软件能够通过Internet来访问并使用这项在线服务。
2. 用简单点的话说,就是系统对外的接口。
3. 它是一种构建应用程序的普遍模型,可以在任何支持网络通信的操作系统中实施运行;它是一种新的web应用程序分支,是自包含、自描述、模块化的应用,可以发布、定位、通过web调用。
4. WebService是一个应用组件,它逻辑性的为其他应用程序提供数据与服务。各应用程序通过网络协议和规定的一些标准数据格式(HTTP、XML、SOAP)来访问WebService,通过WebService内部执行得到所需结果。Web Service可以执行从简单的请求到复杂商务处理的任何功能。一旦部署以后,其他WebService应用程序可以发现并调用它部署的服务。
5. 数据库系统与各个应用程序的分布式应用模型图
DBA有权限管理内部数据库系统,DBMS对外提供WSDL描述开放的服务,外部各个应用系统(包括.NET、JAVA等架构)通过SOAP协议与DBMS进行交互,形成数据库-程序级SOA系统。
6. 在构建和使用WebService时,主要用到以下几个关键的技术和规则:
1) XML:可扩展标记语言 描述数据的标准方法;
2) SOAP:简单对象访问协议(HTTP消息头添加SOAP支持);
3) WSDL:Web服务描述语言 描述服务的标准方法;
4) UDDI(Universal Description,Discovery and Integration):通用描述、发现与集成,它是一种独立于平台的,基于XML语言的用于在互联网上描述服务的协议。我们可以将自己的WS注册到UDDI服务器被外界访问。
7. WebService完全基于XML(可扩展标记语言)、XSD(XMLSchema)等独立于平台、独立于软件供应商的标准,是创建可互操作的、分布式应用程序的新平台。
8. 在以下三种情况下,使用WebService会带来极大的好处:
1) 跨防火墙的通信:
如果应用程序有成千上万的用户,而且分布在世界各地,那么客户端和服务器之间的通信将是一个棘手的问题。因为客户端和服务器之间通常会有防火墙或者代理服务器。在这种情况下,使用DCOM(Microsoft Distributed Component Object Model(DCOM)是ComponentObject Model(COM)的扩展,它支持不同的两台机器上的组件间的通信)就不是那么简单,通常也不便于把客户端程序发布到数量如此庞大的每一个用户手中。传统的做法是,选择用浏览器作为客户端,写下一大堆ASP页面,把应用程序的中间层暴露给最终用户。这样做的结果是开发难度大,程序很难维护。
举个例子,在应用程序里加入一个新页面,必须先建立好用户界面(Web页面),并在这个页面后面,包含相应商业逻辑的中间层组件,还要再建立至少一个ASP页面,用来接受用户输入的信息,调用中间层组件,把结果格式化为HTML形式,最后还要把“结果页”送回浏览器。要是客户端代码不再如此依赖于HTML表单,客户端的编程就简单多了。
如果中间层组件换成WebService的话,就可以从用户界面直接调用中间层组件,从而省掉建立ASP页面的那一步。要调用WebService,可以使用自己开发的SOAP客户端,然后把它和应用程序连接起来。不仅缩短了开发周期,还减少了代码复杂度,并能够增强应用程序的可维护性。同时,应用程序也不再需要在每次调用中间层组件时,都跳转到相应的“结果页”。
从经验来看,在一个用户界面和中间层有较多交互的应用程序中,使用WebService这种结构,可以节省花在用户界面编程上20%的开发时间。另外,这样一个由WebService组成的中间层,完全可以在应用程序集成或其它场合下重用。最后,通过WebService把应用程序的逻辑和数据“暴露”出来,还可以让其它平台上的客户重用这些应用程序。
2) 应用程序集成:
企业里经常都要把用不同语言写成的、在不同平台上运行的各种程序集成起来,而这种集成将花费很大的开发力量。应用程序经常需要从运行在IBM主机上的程序中获取数据;或者把数据发送到主机或UNIX应用程序中去。即使在同一个平台上,不同软件厂商生产的各种软件也常常需要集成起来。通过WebService,应用程序可以用标准的方法把功能和数据“暴露”出来,供其它应用程序使用。
3) B2B的集成
跨公司的商务交易集成通常叫做B2B集成。WebService是B2B集成成功的关键。通过WebService,公司可以把关键的商务应用“暴露”给指定的供应商和客户。
当然,这并不是一个新的概念,EDI(电子文档交换)早就是这样了。但是,WebService的实现要比EDI简单得多,而且WebService运行在Internet上,在世界任何地方都可轻易实现,其运行成本就相对较低。不过,WebService并不像EDI那样,是文档交换或B2B集成的完整解决方案。WebService只是B2B集成的一个关键部分,还需要许多其它的部分才能实现集成。但是这样就大大减少了花在B2B集成上的时间和成本,让许多原本无法承受EDI的中小企业也能实现B2B集成。
4) 软件和数据重用:
软件重用是一个很大的主题,重用的形式很多,重用的程度有大有小。最基本的形式是源代码模块或者类一级的重用,另一种形式是二进制形式的组件重用。
图2用WebService集成各种应用中的功能,为用户提供一个统一的界面。
例如,要建立一个局域网上的门户站点应用,让用户既可以查询联邦快递包裹,查看股市行情,又可以管理自己的日程安排,还可以在线购买电影票。现在Web上有很多应用程序供应商,都在其应用中实现了这些功能。一旦他们把这些功能都通过WebService“暴露”出来,就可以非常容易地把所有这些功能都集成到你的门户站点中,为用户提供一个统一的、友好的界面。
当前,像表格控件或用户界面控件这样的可重用软件组件,在市场上都占有很大的份额。但这类软件的重用有一个很大的限制,就是重用仅限于代码,数据不能重用。原因在于,发布组件甚至源代码都比较容易,但要发布数据就没那么容易,除非是不会经常变化的静态数据。
WebService在允许重用代码的同时,可以重用代码背后的数据。使用WebService,再也不必像以前那样,要先从第三方购买、安装软件组件,再从应用程序中调用这些组件;只需要直接调用远端的WebService就可以了。
9. 有一些情况,WebService根本不能带来任何好处:
1) 单机应用程序:
目前,企业和个人还使用着很多桌面应用程序。其中一些只需要与本机上的其它程序通信。在这种情况下,最好就不要用WebService,只要用本地API就可以了。
2) 局域网的同构应用程序:
在许多应用中,所有的程序都是用VB或VC开发的,都在Windows平台下使用COM-组件对象模型,都运行在同一个局域网上。例如,有两个服务器应用程序需要相互通信,或者有一个Win32或WinForm的客户程序要连接局域网上另一个服务器的程序。在这些程序里,使用DCOM-分布式组件对象模型会比SOAP/HTTP有效得多。
2. WebService概括
1. WebService的功能
WebService是一种跨编程语言和跨操作系统平台的远程调用技术。
1) 所谓远程调用,就是一台计算机a上的一个程序可以调用到另外一台计算机b上的一个对象的方法。
2) 什么情况下可能用到远程调用技术呢?例如,amazon,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以webservice服务的形式暴露出来,让第三方网站和程序可以调用这些服务功能,这样扩展了自己系统的市场占有率,往大的概念上吹,就是所谓的SOA应用。
3) 所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。
4) 除了WebService外,常见的远程调用技术还有RMI(Remotemethod invoke)和CORBA,由于WebService的跨平台和跨编程语言特点,因此比其他两种技术应用更为广泛,但性能略低。
2. WebService的调用原理
WebService使用SOAP协议实现跨编程语言和跨操作系统平台。
1) WebService采用HTTP协议传输数据,采用XML格式封装数据(即XML中说明调用远程服务对象的哪个方法,传递的参数是什么,以及服务对象的返回结果是什么)。
2) WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议(simple object accessprotocol,简单对象访问协议)。
3) SOAP协议 = HTTP协议 + XML数据格式。SOAP协议是基于HTTP协议的,两者的关系就好比高速公路是基于普通公路改造的。
4) HTTP协议和XML是被广泛使用的通用技术,各种编程语言对HTTP协议和XML这两种技术都提供了很好的支持,WebService客户端与服务器端使用什么编程语言都可以完成SOAP的功能,所以,WebService很容易实现跨编程语言,跨编程语言自然也就跨了操作系统平台。
3. WebService调用之前的工作
WSDL文件
1) WebService客户端要调用一个WebService服务,首先要有知道这个服务地址在哪,以及这个服务里有什么方法可以调用,所以,WebService服务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。
2) WSDL(webservicedescription language)是基于XML格式的,它是WebService客户端和服务器端都能理解的标准格式,其中描述的信息可以分为what,where,how等部分。
3) WSDL文件保存在Web服务器上,通过一个url地址就可以访问到它。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。
4) WebService服务提供商可以通过两种方式来暴露它的WSDL文件地址:
l 注册到UDDI服务器,以便被人查找;
l 直接告诉给客户端调用者,例如,在自己网站给出信息或邮件告诉。
4. WebService的工作过程
5. WebService的开发应用
WebService开发可以分为服务器端开发和客户端开发两个方面
1) 把公司内部系统的业务对象发布成Web服务,供远程合作单位和个人调用。(借助一些WebService框架可以很轻松地把自己的业务对象发布成WebService服务,Java方面的典型WebService框架包括:jax-wsri、axis、xfire、cxf等,javaee服务器通常也支持发布WebService服务,例如JBoss。这框架应用不是学习的重点,看看相关的技术手册都很轻松地掌握,关键还是要了解WebService的工作原理)。
2) 调用别人发布的WebService服务,大多数人从事的开发都属于这个方面,例如,调用天气预报WebService服务。(使用厂商的WSDL2Java之类的工具生成静态调用的代理类代码;使用厂商提供的客户端编程API类;使用SUN公司早期标准的jax-rpc开发包;使用SUN公司最新标准的jax-ws开发包)。
3) 在客户端,用户可以通过JAX-WS的API创建一个代理(用本地对象来替代远程的服务)来实现对于远程服务器端的调用。
6. WebService的客户端编程原理
代理图
我们给这各类WebService客户端API传递wsdl文件的url地址,这些API就会创建出底层的代理类,我调用这些代理,就可以访问到webservice服务。代理类把客户端的方法调用变成soap格式的请求数据再通过HTTP协议发出去,并把接收到的soap数据变成返回值返回。
7. WebService客户端编程──工具生成静态代理类方式
1) xfire的WSGen
2) axis的WSDL2java
写批处理或直接在eclipse中运行这个类。
3) jdk6的wsimport
4) cxf的…
8. WebService客户端编程──动态代理类方式
动态代理支持在运行时访问服务端点接口 (SEI),不需要静态生成stub类。JAX-WS 实现通过 J2SE 5.0 动态代理功能来处理代理。动态代理是一种类,它实现若干接口。JAX-WS 动态代理总是实现 javax.xml.ws.BindingProvider,因此,与 Dispatch(动态调用接口)客户端一样,代理也称为 BindingProvider。
1) 使用jax-rpc(早期)
l 第一步是创建xxx.Service实例对象。
l 调用getPort返回代理,客户端的接口要实现remote接口,因为客户端代理是用java技术生成的,java生成的远程调用的代理必须实现remote接口,即我实现的代理靠的是java的remote技术。
2) 使用jax-ws
上节提到过,JAX-WS 工具生成一个具体的服务实现类,这个类也称为静态服务(Static Service)。生成的这个类拥有两个公开构造函数,一个没有参数,另一个有两个参数,分别表示 WSDL 位置(java.net.URL)和服务名(javax.xml.namespace.QName)。
服务是 WSDL 服务的一种抽象表示。服务实例可以是动态服务,也可以是静态服务。本文主要关注使用静态服务。
BankingService 的 getAccountsPort() 方法在运行时获取一个 stub。如前所述,对于 WSDL服务中定义的每个端口,生成的服务类都有一个对应的 getPort 方法。
WSDL文件介绍了WSDL故障 InsufficientFunds。这种故障是提现操作的又一个可能结果。根据JAX-WS 规范定义的WSDL到 Java映射规则,WSDL 文档的绑定部分中指定的故障名称后面带有 Exception。这样,InsufficientFunds 故障就成为InsufficientFundsException,该故障就变成了 JavaException。
9. WebService客户端编程──动态调用接口(DII)
1) 使用jax-rpc(早期)
l 第一步是创建xxx.Service实例对象
l 调用getCall返回Call对象,call的invoke实现动态调用。
2) 使用jax-ws
调用WebService的Java客户端一般采用以下三种方式:生成的stub、proxy(动态代理)以及dispatch(动态调用接口)。dispatch又分为payload和message两种方式。下面就dispatch的message方式进行介绍。
public classTestClient {
// 名字空间
public static final String targetNamespace ="http://service.billInface.boss.gmt/";
// 服务名
public static final String serName ="BillWebServiceService";
// 端口名
public static final String pName ="BillWebServicePort";
// 服务地址
public static final String endpointAddress ="http://localhost:8080/BillInface/BillWebServicePort?wsdl";
// 方法名
public static final String OPER_NAME ="getInBillingLoginInfo";
// 参数名
public static final String INPUT_NMAE ="arg0";
public static voidmain(String[] args)throwsException {
QName serviceName = new QName(targetNamespace,serName);
QName portName = new QName(targetNamespace,pName);
javax.xml.ws.Service service =Service.create(serviceName);
service.addPort(portName,SOAPBinding.SOAP11HTTP_BINDING,
endpointAddress);
Dispatch<SOAPMessage>dispatch = service.createDispatch(portName,
SOAPMessage.class,Service.Mode.MESSAGE);
BindingProvider bp =(BindingProvider) dispatch;
Map<String, Object> rc =bp.getRequestContext();
rc.put(BindingProvider.SOAPACTION_USE_PROPERTY,Boolean.TRUE);
rc.put(BindingProvider.SOAPACTION_URI_PROPERTY,OPER_NAME);
MessageFactory factory =((SOAPBinding) bp.getBinding())
.getMessageFactory();
SOAPMessage request =factory.createMessage();
SOAPBody body =request.getSOAPBody();
QName payloadName = new QName(targetNamespace,OPER_NAME,"ns1");
SOAPBodyElement payload =body.addBodyElement(payloadName);
SOAPElement message =payload.addChildElement(INPUT_NMAE);
message.addTextNode("x");
SOAPMessage reply = null;
try {
reply =dispatch.invoke(request);
} catch(WebServiceException wse) {
wse.printStackTrace();
}
SOAPBody soapBody =reply.getSOAPBody();
SOAPBodyElement nextSoapBodyElement= (SOAPBodyElement) soapBody
.getChildElements().next();
SOAPElement soapElement =(SOAPElement) nextSoapBodyElement
.getChildElements().next();
System.out.println("获取回应信息为:" +soapElement.getValue());
}
}
10. WebService框架的底层实现原理
技术实现原理
各类WebService框架的本质就是一个大大的Servlet,当远程调用客户端给它通过http协议发送过来soap格式的http请求数据时,它分析这个数据,就知道要调用哪个java类的哪个方法,于是去查找或创建这个对象,并调用其方法,再把方法返回的结果包装成soap格式的数据,通过http响应消息回给客户端。
3. Axis2
3.1. Axis2 安装配置
1. Axis2下载包
访问http://axis.apache.org/axis2/java/core/download.cgi
2. 说明一下这四个包
1) Standard Binary Distribution
Axis2的完整版,包括一些示例和实用性脚本。
2) WAR (Web Archive) Distribution
Axis2的Web应用包,可以将它部署到大部分Servlet容器中(Tomcat、Weblogic等)。
3) Documents Distribution
Axis2的所有文档包,包括API文档是学习说明文档。
4) Source Distribution
Axis2的源代码包,使用maven工具-Drelease可以获得Standard Binary Distribution。
3. 环境设置
JDK1.5及以上
Tested on Windows XP, Linux, Mac OS X, Fedora core, Ubuntu, Gentoo
Build Tool-Apache Ant 1.6.5及以上:用于运行示例和buildWAR文件
Build Tool- Apache Maven 2.x:从Axis2源码获得StandardBinary Distribution
4. 作为单独服务器安装
1) 下载Axis2 Standard Binary Distribution。
2) 添加环境变量AXIS2_HOME,例如/opt/axis2-1.6.2,添加AXIS2_HOME/bin到path。
3) 启动Axis2服务器
%AXIS2_HOME%\bin\axis2server.bat (Windows)
$AXIS2_HOME/bin/axis2server.sh (Unix)
4) 启动成功后,访问http://localhost:8080/axis2/services/
5) 创建axis2.war
下载并安装apache ant;
执行
生成的axis2.war放在AXIS2_HOME/dist目录下,所有的services和modules放在AXIS2_HOME/repository目录下,它们会被打包到已经创建的axis2.war中,包括Axis2的配置文件AXIS2_HOME/conf/axis2.xml也打包到axis2.war中。
6) 熟悉Axis2的脚本Scripts
Axis2的所有脚本放在AXIS2_HOME/bin目录下。
l axis2server.{sh|bat}:
单独启动Axis2服务器,启动时加载AXIS2_HOME/repository和AXIS2_HOME/conf/axis2.xml配置文件。
如果你想使用单独的Axis2服务器去发布一个service,只需copy你的service包文件到AXIS2_HOME/repository/services目录下,然后在AXIS2_HOME/conf/axis2.xml文件中找到“TransportIns”,然后配置“transport receivers”,默认列出的是8080端口SimpleHTTPServer。
最后重启Axis2服务器。
l wsdl2java.{bat|sh}:
该命令根据给定的WSDL文件生成客户端调用的Java代码(客户端存根stubs)。
例如:
wsdl2java.sh-uri ../wsdl/Axis2Sample.wsdl
l java2wsdl.{bat|sh}:
该命令根据给定的java类文件生成WSDL文件。
5. 安装到Servlet容器中——Tomcat
1) 将axis2.war文件放到tomcat/webapps目录
2) 启动tomcat,访问http://<host :port>/axis2
点击“Validate”,查看是否运行正常
6. 上传Service组件
Axis2 Web应用提供一个接口支持“.aar”文件形式的service上传。
1) 在webapps\axis2\WEB-INF\conf\axis2.xml文件中找到用户名密码登陆后
2) 上传aar文件
已经上传的.aar文件存储在默认的service目录:
<webapps>/axis2/WEB-INF/services,该发布属于热部署。
3.2. Axis2 快速入门
1. 提示
l 该节中的代码都是在示例代码里的,Axis2_HOME/samples/quickstart, quickstartadb, quickstartaxiom,quickstartjibx 和 quickstartxmlbeans。
l 以上都包含一个build.xml文件,需要使用ant来生成工程。
2. 介绍
1) 以StockQuoteService(股票报价)为例,看一下源代码:
package samples.quickstart.service.pojo;
import java.util.HashMap;
public class StockQuoteService { private HashMap map = new HashMap();
public double getPrice(String symbol) { Double price = (Double) map.get(symbol); if(price != null){ return price.doubleValue(); } return 42.00; }
public void update(String symbol, double price) { map.put(symbol, new Double(price)); } } |
2) 以后我们会用四种方式打包该类,并发布到Axis2服务器。
3. 准备工作
1) 安装JDK
2) 安装tomcat
3) 将axis2.war放到tomcat中
4) 配置axis2环境变量
4. 为Service生成WSDL文件
1) 编译StockQuoteService.java为class文件。
2) 使用java2WSDL命令生成WSDL文件
3) 接下来,我们可能要对StockQuoteService.wsdl文件内容进行添加或修改等操作。
5. Axis2.war的目录结构
axis2-web META-INF WEB-INF classes conf axis2.xml lib activation.jar ... xmlSchema.jar modules modules.list addressing.mar ... soapmonitor.mar services services.list aservice.aar ... version.aar web.xml |
l axis2-web文件夹:基本JSP页面集合
l axis2.xml:控制应用如何处理接收到的消息
l arr文件:Service发布文件,结构如下形式:
- StockQuoteService - META-INF - services.xml - lib - samples - quickstart - service - pojo - StockQuoteService.class |
l service.xml文件:用于定义service。
<service name="StockQuoteService" scope="application"> <description> Stock Quote Sample Service </description> <messageReceivers> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> </messageReceivers> <parameter name="ServiceClass"> samples.quickstart.service.pojo.StockQuoteService </parameter> </service> |
l 可以将service以上面的层级关系发布到services目录下,也可以压缩为aar格式然后发布。
6. 创建服务器端Service
下面我们用5种方法创建基于StockQuoteService的service:
使用POJO、AXIOM's OMElement、Axis2 Databinding Framework (ADB)、XMLBeans和JiBX。
1) 使用POJO
创建以上service发布层级目录,然后压缩成StockQuoteService.arr包,复制到webapps/axis2/WEB-INF/services目录,重启tomcat,打开浏览器访问http://localhost:8080/axis2/services/StockQuoteService?wsdl,
http://localhost:8080/axis2/services/StockQuoteService?xsd
发布成功。
或者:
先cd D:\axis2-1.6.2\samples\quickstart
然后ant 得到 build/StockQuoteService.aar
测试如下链接:
http://localhost:8080/axis2/services/StockQuoteService/getPrice?symbol=IBM
得到:
测试如下链接:
http://localhost:8080/axis2/services/StockQuoteService/update?symbol=IBM&price=10
http://localhost:8080/axis2/services/StockQuoteService/getPrice?symbol=IBM
2) 使用AXIOM'sOMElement
AXIOM是Axis 2的XML对象模型,其目标是提供强大的特性组合彻底改变 XML 处理技术,用于处理SOAP文档。AXIOM 超越了现有的 XML 处理技术,它把延迟构建(允许在其他部分还没有完成的时候使用树的一部分)和一种快速、轻型的可定制对象模型结合了起来。AXIOM强大的延迟构建能力源于底层的Streaming API for XML (StAX) 解析器。AXIOM 的内存占用要好于现有多数依靠SAX 和/或 DOM 输入输出的对象模型
l aar层级目录结构:
quickstartaxiom - README.txt - build.xml - resources - META-INF - services.xml - StockQuoteService.wsdl - src - samples - quickstart - service - axiom - StockQuoteService.class |
l service.xml:
<service name="StockQuoteService" scope="application"> <description> Stock Quote Service </description> <operation name="getPrice"> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> </operation> <operation name="update"> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver"/> </operation> <parameter name="ServiceClass">samples.quickstart.service.axiom.StockQuoteService</parameter> </service> |
l 使用AXIOM库开发服务端Service代码
public class StockQuoteService { private HashMap map = new HashMap();
public OMElement getPrice(OMElement element) throws XMLStreamException { element.build(); element.detach();
OMElement symbolElement = element.getFirstElement(); String symbol = symbolElement.getText();
String returnText = "42"; Double price = (Double) map.get(symbol); if(price != null){ returnText = "" + price.doubleValue(); } OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace omNs = fac.createOMNamespace("http://axiom.service.quickstart.samples/xsd", "tns"); OMElement method = fac.createOMElement("getPriceResponse", omNs); OMElement value = fac.createOMElement("price", omNs); value.addChild(fac.createOMText(value, returnText)); method.addChild(value); return method; }
public void update(OMElement element) throws XMLStreamException { element.build(); element.detach();
OMElement symbolElement = element.getFirstElement(); String symbol = symbolElement.getText();
OMElement priceElement = (OMElement)symbolElement.getNextOMSibling(); String price = priceElement.getText();
map.put(symbol, new Double(price)); } } |
l 测试:
http://localhost:8080/axis2/services/StockQuoteService/getPrice?symbol=IBM
得到:
7. 创建客户端Service调用
下面我们用4种方法创建基于StockQuoteService的service客户端调用:
使用AXIOM's OMElement、Axis2 Databinding Framework (ADB)、XMLBeans和JiBX。
1) 使用AXIOM
2) 客户端调用代码
public class AXIOMClient {
private static EndpointReference targetEPR = new EndpointReference("http://localhost:8080/axis2/services/StockQuoteService");
public static OMElement getPricePayload(String symbol) { OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace omNs =
OMElement method = fac.createOMElement("getPrice", omNs); OMElement value = fac.createOMElement("symbol", omNs); value.addChild(fac.createOMText(value, symbol)); method.addChild(value); return method; }
public static OMElement updatePayload(String symbol, double price) { OMFactory fac = OMAbstractFactory.getOMFactory(); OMNamespace omNs =
OMElement method = fac.createOMElement("update", omNs);
OMElement value1 = fac.createOMElement("symbol", omNs); value1.addChild(fac.createOMText(value1, symbol)); method.addChild(value1);
OMElement value2 = fac.createOMElement("price", omNs); value2.addChild(fac.createOMText(value2, Double.toString(price))); method.addChild(value2); return method; }
public static void main(String[] args) { try { OMElement getPricePayload = getPricePayload("WSO"); OMElement updatePayload = updatePayload("WSO", 123.42); Options options = new Options(); options.setTo(targetEPR); options.setTransportInProtocol(Constants.TRANSPORT_HTTP);
ServiceClient sender = new ServiceClient(); sender.setOptions(options);
sender.fireAndForget(updatePayload); System.err.println("price updated"); OMElement result = sender.sendReceive(getPricePayload);
String response = result.getFirstElement().getText(); System.err.println("Current price of WSO: " + response);
} catch (Exception e) { e.printStackTrace(); } }
}// 单独放在Eclipse中运行即可 |
3.3. Axis2 数据绑定
1. 概述
1) Axis2支持便于访问XML数据的各种数据绑定框架。
2) Apache Axis2 Web服务框架一开始就设计用于支持多种 XML 数据绑定方法。当前的版本提供对 XMLBeans 和 JiBX 数据绑定以及专门针对 Axis2 开发的自定义 Axis 数据绑定(Axis Data Binding,ADB)的全面支持。本文将说明如何将这些不同的数据绑定方法与 Axis2结合使用,并说明为什么可能会为应用程序优先选择其中的一种方法。
3) XML消息交换是Web服务的核心,应用程序需要将 XML 转换为其内部数据结构(或反向转换),以便在应用程序内使用此数据。
4) 数据绑定 是指处理 XML 和应用程序数据结构间的这种转换的技术。可以为应用程序编写自定义数据绑定代码,但大部分开发人员发现使用数据绑定框架更为方便,此类框架将以通用的方式处理此转换工作,适用于各种应用程序。
5) 我们已经了解了 Axis2 用于 XML 消息绑定的AXIOM文档模型。AXIOM 与其他文档模型的不同之处在于,它支持根据需要绑定模型,而不用一次性完成此工作。
6) 为了隔离应用程序,避免直接使用 AXIOM 的情况,Axis2 支持从 Web 服务描述语言(Web Services Description Language,WSDL)生成链接代码。所生成的链接代码使用所选数据绑定框架处理数据结构与 XML 之间的转换细节,让您的应用程序直接访问数据结构。
7) Axis2 可为服务客户机和服务提供者生成链接代码。客户机链接代码采用存根类(存根类是一个类,它实现了一个接口,但是实现后的每个方法都是空的)的形式,始终从Axis2org.apache.axis2.client.Stub 类进行扩展。提供者(或服务器)链接代码采用服务特定的实现框架的形式提供,并提供实现org.apache.axis2.engine.MessageReceiver 接口的消息接收器类。客户机和服务器链接代码生成工作都由WSDL2Java工具进行处理。
8) 客户端存根代码为应用程序代码定义访问方法,以调用服务操作。
l 客户端首先要创建存根类的实例
l 选择使用 org.apache.axis2.client.Stub 基类定义的方法来配置各个功能。
l 调用服务特定的访问方法来实际调用操作。
9) 客户端存根代码分为同步调用和异步调用
10) 服务器端链接代码是作为 Axis2 服务器配置的一部分定义的消息接收器类。此消息接收器必须实现org.apache.axis2.engine.MessageReceiver 接口。此接口定义单个 voidreceive(org.apache.axis2.context.MessageContext) 方法。在接收到请求消息时,Axis2框架将调用此方法,然后由此方法负责处理请求的所有处理工作(包括在合适的情况下生成响应)。
11) 服务器端连接代码包含Skeleton(框架)接口和实现类。框架实现类中定义与XML数据绑定的各类Bean。
2. 从 WSDL 生成代码——完成服务器/客户端的数据绑定
1) Axis2 提供了WSDL2Java工具,用于从 WSDL 服务定义生成代码。可以通过将 org.apache.axis2.wsdl.WSDL2Java 类作为 Java 应用程序运行来直接使用此工具,也可以通过 Ant 任务、Maven 插件或 Eclipse 或 IDEA 插件。拥有这么多选择的缺点在于,从功能和错误修补方面而言,备选方案通常滞后于基本 Java 应用程序,因此通常可能最好直接运行 Java 应用程序(本文将对此进行讨论)。
2) WSDL2java命令
WSDL2Java 提供很多不同的命令行选项,而且选项的数量还会随着时间的增加而增加。Axis2 文档包括了选项的完整参考,这里将仅仅讨论一些最为重要的内容:
· -o path — 设置用于输出类和文件的目标目录(缺省输出到工作目录)
· -p package-name — 设置生成的类的目标包(缺省为从 WSDL 命名空间生成)
· -d name — 设置数据绑定框架(adb 表示 ADB,xmlbeans 表示 XMLBeans,jibx 表示 JiBX 以及 none 表示无数据绑定;adb 为缺省选项)
· -uw — 取消 doc/lit-wrapped 消息的包装,仅适用于受支持的框架(目前包括 ADB 和 JiBX)
· -s — 仅生成同步客户机接口
· -ss — 生成服务器端代码
· -sd — 生成服务器端部署文件
· -uri path — 为要生成的服务设置指向 WSDL 的路径
%AXIS2_HOME%\bin\wsdl2java -uri resources\META-INF\StockQuoteService.wsdl -p samples.quickstart.service.adb -dadb-s -ss -sd -ssi -o build\service |
3) ADB数据绑定
ADB 是 Axis2 的数据绑定扩展。与其他数据绑定框架不同,ADB 代码仅可用于 Axis2 Web 服务。这个限制是 ADB 的一大局限,但也带来了一些好处。由于 ADB 与 Axis2 进行了集成,因此其代码可针对 Axis2 要求进行优化。这方面的一个例子就是,ADB 构建于位于 Axis2 核心的 AXis 对象模型(AXis Object Model,AXIOM)文档模型之上。
l 服务端数据绑定过程
使用WSDL2Java工具生成框架skeleton
打开D:\build
下一步修改生成的框架skeleton,添加方法的自定义内容:
D:\build\src\test\adb\StockQuoteServiceSkeleton.java
public class StockQuoteServiceSkeleton { private static HashMap map; static{ map = new HashMap(); } public void update(Update param0) { map.put(param0.getSymbol(), new Double(param0.getPrice())); }
public GetPriceResponse getPrice(GetPrice param1) { Double price = (Double) map.get(param1.getSymbol()); double ret = 42; if(price != null){ ret = price.doubleValue(); } GetPriceResponse res =new GetPriceResponse(); res.set_return(ret); return res; } } |
执行cd build、ant命令后,将D:\build\build\lib\StockQuoteService.aar部署到tomcataxis2服务器后,测试:
http://localhost:8080/axis2/services/StockQuoteService/getPrice?symbol=IBM
如果没有以上实现代码则会得到:
需要实现getPrice方法:
l 客户端数据绑定过程
使用WSDL2Java工具生成存根类stub
打开D:\build
下一步编写客户端调用存根stub的主函数并运行:(服务方法嵌入在消息类中)
public class ADBClient{ public static void main(java.lang.String args[]){ try{ StockQuoteServiceStub stub =new StockQuoteServiceStub ("http://localhost:8080/axis2/services/StockQuoteService");
getPrice(stub); update(stub); getPrice(stub);
} catch(Exception e){ e.printStackTrace(); System.err.println("\n\n\n"); } }
/* fire and forget */ public static void update(StockQuoteServiceStub stub){ try{ StockQuoteServiceStub.Update req = new StockQuoteServiceStub.Update(); req.setSymbol ("ABC"); req.setPrice (42.35);
stub.update(req); System.err.println("price updated"); } catch(Exception e){ e.printStackTrace(); System.err.println("\n\n\n"); } }
/* two way call/receive */ public static void getPrice(StockQuoteServiceStub stub){ try{ StockQuoteServiceStub.GetPrice req = new StockQuoteServiceStub.GetPrice();
req.setSymbol("ABC");
StockQuoteServiceStub.GetPriceResponse res =stub.getPrice(req);
System.err.println(res.get_return()); } catch(Exception e){ e.printStackTrace(); System.err.println("\n\n\n"); } } } |
ADB取消包装
向 WSDL2Java 传递了 -uw 参数,以生成取消包装接口。以上增加的复杂性层次的消息类几乎都去掉了,服务方法直接接受参数和返回值,而不是嵌入在消息类中。实际上,ADB仍然生成消息类,并在生成的代码中使用这些类,但代码通常会忽略这些类,而直接使用数据。
public class ADBClient{ public static void main(java.lang.String args[]){ try{ StockQuoteServiceStub stub =new StockQuoteServiceStub ("http://localhost:8080/axis2/services/StockQuoteService");
getPrice(stub); update(stub); getPrice(stub);
} catch(Exception e){ e.printStackTrace(); System.err.println("\n\n\n"); } }
/* fire and forget */ public static void update(StockQuoteServiceStub stub){ try{ stub.update(“ABC”,42.35); System.err.println("price updated"); } catch(Exception e){ e.printStackTrace(); System.err.println("\n\n\n"); } }
/* two way call/receive */ public static void getPrice(StockQuoteServiceStub stub){ try{ System.err.println(stub.getPrice()); } catch(Exception e){ e.printStackTrace(); System.err.println("\n\n\n"); } } } |
3. 其他XMLBeans和JiBX方式同理需要命令生成服务器框架和客户端存根,配置和使用比较复杂,此处略说,但值得注意的是XMLBeans没有取消包装功能。
3.4. Axis2配置文件
Axis2提供三种配置文件:全局配置axis2.xml,服务配置services.xml,模块配置module.xml。
1. 全局配置axis2.xml
在axis2.xml文件中,根元素<axisconfigname="AxisJava2.0">下的顶级元素有六个,它们分别为:
Parameter
1) Parameter
parameter元素是name-value对,每一个可用的parameter都会被转换成AxisConfiguration中的属性,因此axis2.xml文档中的parameter元素都可以通过AxisConfiguration在运行系统中访问到,争取的配置方法是:
<parameter name="name of the parameter" >parameter value </parameter> |
2) TransportReceiver
接收器,可以定义不多不同的传输接收器,正确的添加方法是:
<transportReceiver <parameter name="port" >6060</parameter> </transportReceiver> |
name属性:传输接收器类型,可以是HTTP,TCP, SMTP, CommonsHTTP等。
class属性:传输接收器具体实现类。
3) TransportSender
和传输接收器一样,可以注册传输发送器,运行时系统使用Sender发送消息(Message),如果axis2运行在tomcat下,则可以使用TCP发送器发送消息而不用HTTP,正确的配置方法是:
<transportSender <parameter name="PROTOCOL" locked="xsd:false">HTTP/1.0</parameter> </transportSender> |
name属性:可以是local、http、tcp、https等。
4) PhaseOrder
表示phase在执行链中的顺序。
<phaseOrder type="InFlow"> <!-- System predefined phases --> <phase name="Transport"> <handler name="RequestURIBasedDispatcher" class="..."> <order phase="Transport"/> </handler> <handler name="SOAPActionBasedDispatcher" class="..."> <order phase="Transport"/> </handler> </phase> </phaseOrder> |
type属性:定义流的类型,InFlow、OutFlow、InFaultFlow、OutFaultFlow
5) Module
使用一个系统级全局Module。
<module ref="addressing"/> |
6) Listeners(Observers)
在Axis2中,AxisConfiguration是可监测的,可以注册进观察者,当AxisConfiguration发生改变时系统将会自动通知。触发事件如下:
部署一个服务时、删除一个服务
激活一个服务或是将服务变成非激活
部署模块、删除模块
注册对于那些附加特性的服务注册观察者是很有用的,如RSS feed生成器将为用户提供服务信息。注册观察者格式:
<listener class=”org.apache.axis2.ObserverIMPL”> <parameter name=”RSS_URL”>http://127.0.0.1/rss</parameter> </listener> |
其中的class代表 observer类的实现,并且需要说明的是类的实现要实现AxisObserver 接口,类要能在classpath中获得。
2. 服务配置services.xml
1) 使用services.xml来对service进行描述,每一个arr包文件都需要有一个services.xml文件,以便验证service,一般位于arr文件META-INF目录下。
2) services.xml示例
<service name="name of the service" scope="name of the scope" class="full qualifide name the service lifecycle class" targetNamespace="target namespase for the service"> <Description> The description of the service </Description>
<transports> <transport>HTTP</transport> </transports>
<schema schemaNamespace="schema namespace"/>
<messageReceivers> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> </messageReceivers>
<parameter name="ServiceClass" locked="xsd:false"> org.apache.axis2.sample.echo.EchoImpl</parameter>
<operation name="echoString" mep="operation MEP"> <actionMapping>Mapping to action</actionMapping> <module ref=" a module name "/> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> </operation> </service> |
name:服务名,如果arr文件名只包含一个service,则与服务名相同。
scope(可选):服务的运行时生命周期,可以是"application", "soapsession","transportsession", "request",默认是"request"。
class(可选):服务生命周期实现类。
targetNamespace(可选):服务的命名空间。
transports(可选):指定哪个服务将被暴露。
parameter:规范工作的服务类,被MessageReceiver装载。
operation:如果服务的实现类是java,那么所有的public方法都会暴露,如果用户想去覆盖可以添加该标签。如果非java服务则必须使用该标签暴露服务的操作。用户也可以为操作定义messageReceiver,否则使用默认的。
3. 模块配置module.xml
1) 使用module.xml来对module进行描述,每一个module包文件都需要有一个module.xml,以便验证service,一般位于包文件META-INF目录下。
2) Module.xml示例
<module class="org.apache.module.Module1Impl"> <InFlow> . </InFlow> <OutFlow> . </OutFlow>
<OutFaultFlow> . </OutFaultFlow>
<InFaultFlow> . </InFaultFlow>
<operation name="creatSeq" mep="MEP_URI_IN_OUT"> <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> <parameter name="para1" locked="xsd:true">10</parameter> </operation> </module> |
3) Class(可选):表示module的实现类,如果没有class属性则该module是一个handlers的集合。否则必须实现org.apache.axis2.modules.Module接口,在部署时其init方法将被调用。
4) Flow:用户可以在flow中添加任意的handler。
5) Operation:在module中添加操作。