Java远程通信技术——Axis实战
前言
在 Internet 网络覆盖全球的今天,网络通信已经是当今软件开发过程中离不开的话题。在常用的Windows、Linux、Unix 系统当中,大部分的网络数据传输都是使用 TCP/IP、UDP/IP 作为底层传输协议的,而 HTTP 协议就是基于 TCP/IP 协议而运行的超文本传送协议。
在 JAVA 高级开发语言中,陆续出现 RMI、CORBA、JAX-RPC、JAX-WS、Axis、XFire、HTTP Invoker、Hessian、Burlap、JMX 等远程通信架构去实现系统之间数据传送。在 “远程通信技术” 的一系列文章中,本人将对上述复杂的 JAVA 远程通信技术作出归纳。
首先,在本篇文章中先对有着10多年历史的 Axis 进行介绍。
目录
一、Axis 简介
二、Axis 1.x 实例
三、Web 服务会话管理
四、自定义Handler
五、新一代 SOAP 引擎 Axis 2.x
六、AXIOM 对象模型
七、Module 模块独立化处理方式
八、异步调用Web服务
一、Axis 简介
1.1 Web 服务的起源
Web 服务是现今实现网络服务概念的趋势,它把基础架构建立于标准化的XML语言之上,能够使用一种与平台无关的方式对数据进行编码,其中 SOAP 与 WSDL 都遵从此标准化的 XML 编码规则。
SOAP (Simple Object Access Protocol,简单对象访问协议)是一种轻量的、简单的、基于 XML 的协议,用于描述在服务过程中服务器端与客户端之间所交换的消息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。
WSDL (Web Service Definition Language,Web服务描述语言)是一种基于 XML的协议,用于定义服务端与客户端之间的契约,描述Web服务的公共接口,列出 Web服务进行交互时需要绑定的协议和信息格式。
Web 服务采用 WSDL 语言描述该服务支持的操作和信息,运行时再将实际的数据以 SOAP 方式在服务端与客户端进行信息传递。
由于软件开发平台众多,当中存在不同的开发风格,当服务器端与客户端使用不同的开发工具时,数据转换成为复杂且关键的问题。而 SOAP 与 WSDL 的主要特性之一在于它们都是可扩展的,且与开发平台无关。为了建立统一的 XML 协议, 微软、IBM、Sun、Oracle、BEA 等多家软件开发商联合起来,组成了一个名为WS-I(Web Service Interoperability)组织,由该组织制定 WS-ReliableMessage、WS-Discovery、WS-Federation、WS-Coordination、WS-AtomicTransaction、WS-BusinessActivity 、WS-Enumeration、WS-Eventing 、WS-Management 等一系列用于数据交换的规范。
1.2 JAX-RPC 、JAX-WS 简介
JAX-RPC ( Java API for XML-based RPC ) 是 Java 库中基于 XML 远程服务的一组标准 API,它通过 WSDL 方式对所提供的服务进行描述,并以 RPC 的风格把 SOAP 信息进行公开,是 Java 库中最早对 Web 服务提供支持的一组API。
JAX-RPC 1.0 从其名称可以看出,最初的目的只是为了支持使用(Remote Procedure Call,RPC) 的 XML 远程过程调用操作,它以 BP 1.0 (WS-I’s Basic Profile 1.0)为基础,依赖于SAAJ 1.2(SOAP with Attachments API for Java)为规范,虽然支持 SOAP 协议,但对 Web 服务功能有一定的局限性。于是在 2003 年底,开发团队对 JAX-RPC 1.0 进行大幅修订,由 Sun 公司组织了一个专家组开始进行 JAX-RPC 2.0 规范的开发。
JAX-RPC 2.0 是基于 JAVA 5 而开发的,它依赖于 Annotation 等新特性,在JAX-RPC 的基础上提供还增加了如异步回调,面向消息等新增技术。JAX-RPC 2.0 以 BP 1.1(WS-I’s Basic Profile 1.1 ) 为基础,依赖于SAAJ 1.3(SOAP with Attachments API for Java)为规范,能使用 SOAP 1.1、SOAP 1.2 进行信息公开。它是 JAX-RPC 1.1 架构发展的成果,在开发完成后,JAX-RPC 2.0 被正式改名成为 JAX-WS ( Java API for XML-Web Services ) 。
1.3 Axis 概述
Axis 全称 Apache EXtensible Interaction System ( 阿帕奇可扩展交互系统 ) , 它是一个 SOAP 引擎,提供创建 Web 服务的基本框架。Axis 1.x 是基于 JAX-RPC 而实现一个工具包,它可以使用 HTTP、JMS、SMTP 等多种传输方式支持 SOAP 。
Axis 2.x 是新一代的 Axis 引擎,它支持 JAX-WS、JAX-PRC 等 API,并且在Axis 1.x 的基础上增加了灵活数据绑定、异步调用等新增功能,可使用 SOAP 1.1 、SOAP 1.2 协议。在服务请求上,Axis 2.x 支持三种请求-响应模式:In-Only、Robust-In和In-Out,也可支持使用 REST 风格的开发方式。
基本的 Axis Web 服务由四部分组成:Axis Servlet 、Axis 部署描述、 远程服务接口、服务实现类。
Axis Servlet 是 Axis 的核心,它负责 WSDL 基础服务信息的公开,并把 SOAP 请求转化为 Java 方法的调用,最后把返回值转化为 SOAP 。Axis Servlet 隐藏了构建 Web 服务的大量代码,使用开发人员不用直接与 SOAP 打交道便可轻松完成 Web 服务的开发。
Axis 部署描述是一个XML文档,它用于管理 Web 服务的发布,决定哪些服务类需要通过 SOAP 对外公开 。
远程服务接口并非必要的,但在很多的 Web 服务开发过程中都会使用远程服务接口用于对外暴露服务类的方法,在服务器端通过服务实现类去继承实现服务接口。
由于 Axis 1.x 与 Axis 2.x 有各自的特色,下面将分开来介绍。
回到目录
二、Axis 1.x 实例
2.1 Axis 1.x 的下载与安装
Axis 1.x 可于官网 http://axis.apache.org/axis/ 下载,完成下载后建立 Web Project 作为测试项目 ,把 lib 文件夹下的 jar 文件拷贝,引入到测试项目当中。
在 web.xml 文件下加入 AxisServlet 配置后,系统就会对以后缀为 *.jws 及路径为 /services/* 的请求进行监听,遇到此类请求时将把信息交由 org.apache.axis.transport.http.AxisServlet 进行处理。
1 <web-app> 2 <display-name>Apache-Axisdisplay-name> 3 <listener> 4 <listener-class>org.apache.axis.transport.http.AxisHTTPSessionListenerlistener-class> 5 listener> 6 <servlet> 7 <display-name>Apache-Axis Servletdisplay-name> 8 <servlet-name>AxisServletservlet-name> 9 <servlet-class> 10 org.apache.axis.transport.http.AxisServlet 11 servlet-class> 12 servlet> 13 14 <servlet> 15 <display-name>Axis Admin Servletdisplay-name> 16 <servlet-name>AdminServletservlet-name> 17 <servlet-class> 18 org.apache.axis.transport.http.AdminServlet 19 servlet-class> 20 <load-on-startup>100load-on-startup> 21 servlet> 22 23 <servlet> 24 <display-name>SOAPMonitorServicedisplay-name> 25 <servlet-name>SOAPMonitorServiceservlet-name> 26 <servlet-class> 27 org.apache.axis.monitor.SOAPMonitorService 28 servlet-class> 29 <init-param> 30 <param-name>SOAPMonitorPortparam-name> 31 <param-value>5001param-value> 32 init-param> 33 <load-on-startup>100load-on-startup> 34 servlet> 35 <servlet-mapping> 36 <servlet-name>AxisServletservlet-name> 37 <url-pattern>/servlet/AxisServleturl-pattern> 38 servlet-mapping> 39 <servlet-mapping> 40 <servlet-name>AxisServletservlet-name> 41 <url-pattern>*.jwsurl-pattern> 42 servlet-mapping> 43 <servlet-mapping> 44 <servlet-name>AxisServletservlet-name> 45 <url-pattern>/services/*url-pattern> 46 servlet-mapping> 47 <servlet-mapping> 48 <servlet-name>SOAPMonitorServiceservlet-name> 49 <url-pattern>/SOAPMonitorurl-pattern> 50 servlet-mapping> 51 web-app>
而 SOAPMonitorService 并非必要配置,但加入SOAPMonitorService 配置,可以便于对服务运行时所传输的SOAP信息进行监听,在服务的requestFlow或responseFlow 中加入 SOAPMonitorHandler , 系统就可显示服务请求和回发时的 SOAP 信息。
1 <deployment xmlns="http://xml.apache.org/axis/wsdd/" 2 xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> 3 <handler name="soapmonitor" type="java:org.apache.axis.handlers.SOAPMonitorHandler"> 4 <parameter name="wsdlURL" value="/axis/SOAPMonitorService-impl.wsdl"/> 5 <parameter name="namespace" 6 value="http://tempuri.org/wsdl/2001/12/SOAPMonitorService-impl.wsdl"/> 7 <parameter name="serviceName" value="SOAPMonitorService"/> 8 <parameter name="portName" value="Demo"/> 9 handler> 10 <service name="SOAPMonitorService" provider="java:RPC"> 11 <parameter name="allowedMethods" value="publishMessage"/> 12 <parameter name="className" value="org.apache.axis.monitor.SOAPMonitorService"/> 13 <parameter name="scope" value="Application"/> 14 service> 15 16 <service name="myService" provider="java:RPC"> 17 <requestFlow> 18 <handler type="soapmonitor"/> 19 requestFlow> 20 <responseFlow> 21 <handler type="soapmonitor"/> 22 responseFlow> 23 <parameter name="allowedMethods" value="*"/> 24 <parameter name="className" value="axis.server.myService"/> 25 service> 26 deployment>
2.2 调用服务的三种方式
下面从最简单的 HelloWorld 开始,介绍 Axis 的使用方法。首先在 WEB-INF 文件夹下建立 server-config.wsdd 文件,在 Axis 1.x 当中,此文件正是用于管理服务发布的默认配置文件。首先 service 用于定义对外暴露的服务,其中 name 属性用于定义服务的名称。像下面例子,当 name 为 PersonService 时,对外暴露的服务路径则对应为 http://localhost:8080/axis.server/services/PersonService 。
而 parameter 用于定义服务的相关属性,className 表示此服务的实现类,而 allowedMethods 表示所公开的服务方法,“*" 则默认为公开此类中的所有 public 公共方法。而 scope 则是用于定义服务对象生成的方式,它包括三个选项:request、session、application 。
request 是默认选择,表示为每个请求生成一个服务对象;
session 表示对同一个客户代理对象所发送的请求使用同一个服务对象,并把服务信息放在同一个上下文当中。当使用有状态服务时,使用此 session 更为合适,在下节将再作进一步介绍;
application 类似于使用单体模式,表示所示的请求均使用同一个服务对象,当使用无状态服务时使用 application 能有效提高运行效率。
最后在 transport 中定义一个 requestFlow 处理类 org.apache.axis.handlers.http.URLMapper,表示在系统接受到 http 请求时,将会调用 URLMapper 类来处理路径映射等问题。
1 <deployment xmlns="http://xml.apache.org/axis/wsdd/" 2 xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> 3 4 <service name="PersonService" provider="java:RPC"> 5 6 <parameter name="className" value="axis.serviceImpls.PersonServiceImpl" /> 7 <parameter name="allowedMethods" value="*" /> 8 <parameter name="scope" value="application" /> 9 service> 10 11 <transport name="http"> 12 <requestFlow> 13 <handler type="java:org.apache.axis.handlers.http.URLMapper"/> 14 requestFlow> 15 transport> 16 deployment>
PersonService 服务代码如下,此时运行服务,输入路径 http://localhost:8080/axis.server/services/PersonService?wsdl ,浏览器上将显示此服务的 wsdl 信息。
1 public interface IPersonService { 2 String HelloWorld(String name); 3 } 4 5 public class PersonServiceImpl implements IPersonService { 6 @Override 7 public String HelloWorld(string name){ 8 return "Hello "+name; 9 } 10 }
客户端的生成工具有多种,其中一种是使用 Axis 1.x 中的自带生成器 WSDL2Java ,此生成器可以根据 wsdl 文件生成客户端。
首先在环境变量中把 Axis_Home 绑定到 Axis 1.x 的根目录,在 path 加入设置 ".;%Axis_Home%\lib ", 然后输入
Java org.apache.axis.wsdl.WSDL2Java http://localhost:8080/axis.server/services/PersonService?wsdl -p axis.client.person
此时系统将在 axis.client.person 包内生成客户端代码类 PersonServiceImpl、PersonServiceImplService、PersonServiceImplServiceLocator、PersonServiceSoapBindingStub。
PersonServiceImplServiceLocator 用于实现 PersonServiceImplService 接口,它绑定了服务的名称,地址,端口等详细资料。
PersonServiceImpl 用于定义服务的接口,而 PersonServiceSoapBindingStub 则是此服务代理,它通过 SOAP 协议把操作请求发送到服务器,并把返回信息转化为 Java 对象。
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 // TODO Auto-generated method stub 4 HelloWorld(); 5 } 6 7 private static void HelloWorld() throws 8 RemoteException, MalformedURLException{ 9 PersonServiceImpl personService=new PersonServiceSoapBindingStub( 10 new URL("http://localhost:8080/axis.server/services/PersonService"), 11 new PersonServiceImplServiceLocator()); 12 System.out.println(personService.helloWorld("Leslie")); 13 }
通过系统自动生成的代理类就能简单地调用远程服务,这是因为在代理类中已经完成了大量关于PersonService 服务的配置,但本人觉得想要深入地了解 Axis 的开发,就应该了解其内部的结构。所以在下面例子当中将介绍如何使用Axis的内部机制直接调用Web服务。
在 org.apache.axis.client 中存在着服务的基础类 Service ,通过 Service 可用于管理服务的绑定地址,端点,获取服务的 WSDL 等详细信息。另外 Call 类用于管理每个服务请求动作,它可以设置每个请求的方法,最后通过call.invoke(Object[])调用服务,并获取完成后的返回值。
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 HelloWorld(); 4 } 5 6 private static void HelloWorld() throws 7 ServiceException,RemoteException{ 8 //生成服务对象Service 9 Service service=new Service(); 10 Call call=(Call) service.createCall(); 11 //设置Endpoint地址 12 call.setTargetEndpointAddress( 13 "http://localhost:8080/axis.server/services/PersonService"); 14 //绑定请求方法名称 15 call.setOperationName("HelloWorld"); 16 //通过call.invoke调用服务,获取返回值 17 String data=(String)call.invoke(new Object[]{"Leslie"}); 18 System.out.println(data); 19 }
如果觉得使用 Call 实现请求较为麻烦,Service 中还提供一个 getPort 方法,通过此方法还可直接实现服务接口 PersonServiceImpl。另外,Axis 还为准备了一个 ServiceFactory 工厂,通过 ServiceFactory 可以直接获取 Service 对象。
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 // TODO Auto-generated method stub 4 HelloWorld(); 5 } 6 7 private static void HelloWorld() throws 8 ServiceException, RemoteException, MalformedURLException { 9 String wsdl="http://localhost:8080/axis.server/services/PersonService?wsdl"; 10 String uri="http://localhost:8080/axis.server/services/PersonService"; 11 String serviceName="PersonServiceImplService"; 12 13 //使用serviceFacotry直接生成服务 14 ServiceFactory factory=ServiceFactory.newInstance(); 15 Service service=(Service) factory.createService( 16 new URL(wsdl),new QName(uri,serviceName)); 17 18 //使用service.getPort方法实现服务接口 19 PersonServiceImpl personService=(PersonServiceImpl)service 20 .getPort(PersonServiceImpl.class); 21 String data=personService.helloWorld("Leslie"); 22 System.out.println(data); 23 }
2.3 以自定义对象传输数据
若需要以自定义对象作为数据传输的载体,则需要为自定义对象继承 Serializable 接口。另外可以留意一下服务的 wsdl , 因为 Axis 并没有默认使用List , Map 等类型, 在 List,Map 等作为参数时,wsdl 都会把返回类型设置为 ArrayOf_xsd_anyType,所以建议使用简单数组作为返回值。
1 public class PersonEntity implements Serializable { 2 private Integer id; 3 private String name; 4 private Integer age; 5 private String address; 6 7 public PersonEntity(Integer id,String name,Integer age,String address){ 8 this.id=id; 9 this.name=name; 10 this.age=age; 11 this.address=address; 12 } 13 14 public Integer getId(){ 15 return id; 16 } 17 18 public void setId(Integer id){ 19 this.id=id; 20 } 21 .......... 22 } 23 24 public interface IPersonService { 25 PersonEntity GetPerson(int id); 26 PersonEntity[] GetList(); 27 List GetList(String name); 28 } 29 30 public class PersonServiceImpl implements IPersonService { 31 @Override 32 public PersonEntity[] GetList(){ 33 PersonEntity[] list=new PersonEntity[2]; 34 PersonEntity person1=new PersonEntity(1,"Leslie",32,"tianhe"); 35 PersonEntity person2=new PersonEntity(2,"Elva",31,"henan"); 36 list[0]=person1; 37 list[1]=person2; 38 return list; 39 } 40 41 @Override 42 public List GetList(String name){ 43 List list=new ArrayList(); 44 PersonEntity person1=new PersonEntity(1,name+" Lee",32,"tianhe"); 45 PersonEntity person2=new PersonEntity(2,name+" Chen",31,"henan"); 46 list.add(person1); 47 list.add(person2); 48 return list; 49 } 50 51 @Override 52 public PersonEntity GetPerson(int id){ 53 return new PersonEntity(id,"Leslie",32,"tianhe"); 54 } 55 }
在 server-config.wsdd 中使用 beanMapping 加入自定义对象绑定,以 languageSpecificType 绑定类名,qname 可由用户设置,但必须与 xmln 特性相对应。
1 ......... 2 <service name="PersonService" provider="java:RPC"> 3 <parameter name="className" value="axis.serviceImpls.PersonServiceImpl" /> 4 <parameter name="allowedMethods" value="*" /> 5 <parameter name="scope" value="application" /> 6 <beanMapping qname="myNS:PersonEntity" xmlns:myNS="urn:PersonEntity" 7 languageSpecificType="java:axis.entity.PersonEntity" /> 8 service> 9 .........
以 WSDL2Java 生成客户端代码后,可以留意 PersonEntity 对象已经自动实现了 Serializable 接口,增加了 getDeserializer、getSerializer 等序列化与反序列化方法。此时直接使用代理类,会自动地完成对象序列化的过程,可以节省了不少时间。
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 GetList(); 4 GetPerson(); 5 } 6 7 private static void GetPerson() throws 8 RemoteException, MalformedURLException{ 9 PersonServiceImpl personService=new PersonServiceSoapBindingStub( 10 new URL("http://localhost:8080/axis.server/services/PersonService"), 11 new PersonServiceImplServiceLocator()); 12 PersonEntity person=personService.getPerson(1); 13 DisplayPersonProperty(person); 14 } 15 16 private static void GetList() throws 17 ServiceException, RemoteException, MalformedURLException{ 18 PersonServiceImpl personService=new PersonServiceSoapBindingStub( 19 new URL("http://localhost:8080/axis.server/services/PersonService"), 20 new PersonServiceImplServiceLocator()); 21 Object[] objs=personService.getList("Leslie"); 22 for(Object person:objs) 23 DisplayPersonProperty((PersonEntity)person); 24 } 25 26 //显示对象属性 27 private static void DisplayPersonProperty(PersonEntity person){ 28 System.out.println("Id:"+person.getId()+" Name:"+person.getName()+" Age:"+ 29 person.getAge()+" Address:"+person.getAddress()); 30 }
但需要注意,如果使用 Service 类去调用服务的时候,需要使用 Call.registerTypeMapping 注册一个类型,把接收到的信息转换为 PersonEntity 类型。在注册类型时 namespaceURI 参数值需要与服务端 server-config.wsdd 中的值保持一致。
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 // TODO Auto-generated method stub 4 GetArray(); 5 GetList(); 6 } 7 8 private static void GetArray() throws 9 ServiceException, RemoteException{ 10 Service service=new Service(); 11 Call call=(Call)service.createCall(); 12 call.setTargetEndpointAddress( 13 "http://localhost:8080/axis.server/services/PersonService"); 14 15 //注册返回类型,namespaceURI 必须与服务端注册值一致 16 QName qName2=new QName("urn:PersonEntity","PersonEntity"); 17 call.registerTypeMapping(PersonEntity.class, qName2, 18 new BeanSerializerFactory(PersonEntity.class,qName2), 19 new BeanDeserializerFactory(PersonEntity.class,qName2)); 20 21 //绑定请求方法 22 call.setOperation("GetList"); 23 24 //设置返回类型 25 call.setReturnClass(PersonEntity[].class); 26 PersonEntity[] list=(PersonEntity[]) call.invoke(new Object[]{}); 27 for(PersonEntity person:list) 28 DisplayPersonProperty(person); 29 } 30 31 private static void GetList() throws 32 ServiceException, RemoteException{ 33 Service service=new Service(); 34 Call call=(Call)service.createCall(); 35 call.setTargetEndpointAddress( 36 "http://localhost:8080/axis.server/services/PersonService"); 37 38 //注册返回类型,namespaceURI 必须与服务端注册值一致 39 QName qName2=new QName("urn:PersonEntity","PersonEntity"); 40 call.registerTypeMapping(PersonEntity.class, qName2, 41 new BeanSerializerFactory(PersonEntity.class,qName2), 42 new BeanDeserializerFactory(PersonEntity.class,qName2)); 43 44 //绑定请求方法 45 call.setOperationName(new javax.xml.namespace.QName( 46 "http://serviceImpls.axis", "GetList")); 47 //输入参数 48 Object[] list=(Object[]) call.invoke(new Object[]{"Leslie"}); 49 for(Object person:list) 50 DisplayPersonProperty((PersonEntity)person); 51 } 52 53 private static void DisplayPersonProperty(PersonEntity person){ 54 System.out.println("Id:"+person.getId()+" Name:"+person.getName()+" Age:"+ 55 person.getAge()+" Address:"+person.getAddress()); 56 }
回到目录
三、Web服务会话管理
记得在第二节曾经为大家介绍服务对象的生成方式,当 scope 设置 session 时,系统会对同一个客户代理对象所发送的请求使用同一个服务对象,并把服务信息放在同一个上下文当中。利用 session 可以把用户名、用户密码、订单号此类信息在方法中传播,也可以确保不同的客户信息分别保存在不同的上下文之上。session 数据的保存时间可以通过 session.setTimeout 方法设置。
1 public interface ILoginService { 2 public Boolean Login(String name,String password); 3 public String GetUserName(); 4 } 5 6 public class LoginServiceImpl implements ILoginService { 7 8 @Override 9 public Boolean Login(String name, String password) { 10 // TODO Auto-generated method stub 11 MessageContext context=MessageContext.getCurrentContext(); 12 Session session=context.getSession(); 13 if(session!=null) 14 context.getSession().set("User",name); 15 return true; 16 } 17 18 @Override 19 public String GetUserName(){ 20 MessageContext context=MessageContext.getCurrentContext(); 21 Session session=context.getSession(); 22 return session.get("User").toString(); 23 } 24 }
在 server-config.wsdd 中,把 scope 设置为 session 模式
1 ....... 2 <service name="LoginService" provider="java:RPC" style="wrapped"> 3 <parameter name="className" value="axis.serviceImpls.LoginServiceImpl" /> 4 <parameter name="allowedMethods" value="*" /> 5 <parameter name="scope" value="session" /> 6 service> 7 .......
在客户端调用时,需要把 maintainSession 设置为 true,此时可以把 userName , password 等信息存到上下文当中。
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 // TODO Auto-generated method stub 4 Login(); 5 } 6 7 private static void Login() throws 8 MalformedURLException, RemoteException{ 9 LoginServiceImpl service1=getService(); 10 LoginServiceImpl service2=getService(); 11 12 service1.login("Leslie", "12345678"); 13 service2.login("Jack", "12345678"); 14 15 System.out.println("UserName: "+service1.getUserName()); 16 System.out.println("UserName: "+service2.getUserName()); 17 } 18 19 private static LoginServiceImpl getService() throws 20 AxisFault, MalformedURLException{ 21 LoginServiceImplServiceLocator locator=new LoginServiceImplServiceLocator(); 22 locator.setMaintainSession(true); 23 LoginServiceImpl loginService=new LoginServiceSoapBindingStub( 24 new URL("http://localhost:8080/axis.server/services/LoginService"), 25 locator); 26 return loginService; 27 }
测试结果
回到目录
四、自定义 Handler
Axis 的 Handler 与 Servlet 中的 Filter 有点相似,用于过滤服务,检测管理 Web 服务信息的接收发送过程。开发 Handler 需要实现 org.apache.axis.Handler 接口,接口包含了下面多个方法:
为了简化 Handler 的开发,Axis 在org.apache.axis.handlers 命名空间内就为客户提供了 BasicHandler、ErrorHandler、LogHandler、SimpleSessionHandler、SimpleAuthenticationHandler、SimpleAuthorizationHandler 等 Handler 用于管理 Axis 的错误处理,日志记录,身份认证,权限管理等工作。BasicHandler 是实现 Handler 接口的基础类,用户可以继承 BasicHandler 类, 开发自定义的 Handler 对特定的服务分别在服务请求request 、信息回送 response 时进行处理。init、invoke 是 BasicHandler 最常用的方法, init 方法会在对象初始化时执行,而对服务的管理操作可以在 invoke 方法中定义。invoke 方法包括了 messageContext 参数 , 利用 messageContext ,可以获取到服务的 SOAP ,HttpServletRequest 、HttpServletResponse、URL 等相关信息。
下面的例子就是利用自定义的 LoginHandler 对 LoginService 服务请求进行监听。
首先修改 server-config.wsdd 文件,在 LoginService 服务的 requestFlow 中加入 LoginHandler 。
1 ....... 2 <service name="LoginService" provider="java:RPC" style="wrapped"> 3 <parameter name="className" value="axis.serviceImpls.LoginServiceImpl" /> 4 <parameter name="allowedMethods" value="*" /> 5 <parameter name="scope" value="session" /> 6 <requestFlow> 7 <handler type="java:axis.handler.LoginHandler"/> 8 requestFlow> 9 service> 10 ......
建立 LoginService,在对 LoginService 服务接收到 SOAP 请求时,系统可以通过 LoginHandler 监听请求信息,获取相关的方法名,输入参数等信息。通过 messageContext.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST) 方法,可以获取到 HttpServletRequest 对象。
1 public interface ILoginService { 2 public Boolean Login(String name,String password); 3 } 4 5 public class LoginServiceImpl implements ILoginService { 6 7 @Override 8 public Boolean Login(String name, String password) { 9 // TODO Auto-generated method stub 10 UserService service=new UserService(); 11 User user=service.getUser(name); 12 if(user!=null) 13 return user.password==password; 14 else 15 return false; 16 } 17 } 18 19 public class LoginHandler extends BasicHandler{ 20 private MessageContext context; 21 22 public void invoke(MessageContext context){ 23 this.context=context; 24 GetServletRequest(); 25 GetSOAP(); 26 } 27 28 //获取HtppServletRequest对象 29 private void GetServletRequest(){ 30 HttpServletRequest request = (HttpServletRequest)context 31 .getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST); 32 String remoteAddress=request.getRemoteAddr(); 33 String method=request.getMethod(); 34 StringBuffer URL=request.getRequestURL(); 35 String message=String.format("Client Address:%s\nMethod:%s\n" + 36 "RequestURL:%s\n", remoteAddress,method,URL); 37 System.out.println(message); 38 } 39 40 //获取SOAPBody信息 41 private void GetSOAP(){ 42 String soap=null; 43 try { 44 soap=context.getCurrentMessage().getSOAPBody().toString(); 45 } catch (SOAPException e) { 46 // TODO Auto-generated catch block 47 e.printStackTrace(); 48 } 49 if(soap!=null){ 50 String[] data= soap.split(">"); 51 for(String line:data) 52 System.out.println(line+">"); 53 } 54 } 55 }
客户端
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 LoginServiceImpl service1=getService(); 4 service1.login("Leslie", "12345678"); 5 } 6 7 private static LoginServiceImpl getService() throws 8 AxisFault, MalformedURLException{ 9 LoginServiceImplServiceLocator locator=new LoginServiceImplServiceLocator(); 10 locator.setMaintainSession(true); 11 LoginServiceImpl loginService=new LoginServiceSoapBindingStub( 12 new URL("http://localhost:8080/axis.server/services/LoginService"), 13 locator); 14 return loginService; 15 }
只要在 service 的 requestFlow 对 Handler 进行绑定,在客户端发送请求后, Handler 就能对服务请求进行监听。
同样地,在 service 的 responseFlow 输入流中对 Handler 进行绑定,就可以对回发的 SOAP 信息进行监听。
下面例子是利用自定义 LoginHandler 对用户的登录回发信息进行监察,计算成功登录的在线人数 。
首先修改 server-config.wsdd 文件,在 LoginService 服务的 responseFlow 中加入 LoginHandler 。
1 <service name="LoginService" provider="java:RPC" style="wrapped"> 2 <parameter name="className" value="axis.serviceImpls.LoginServiceImpl" /> 3 <parameter name="allowedMethods" value="*" /> 4 <parameter name="scope" value="session" /> 5 <responseFlow> 6 <handler type="java:axis.handler.LoginHandler"/> 7 responseFlow> 8 service>
在自定义 Handler 中可以通过 Message.getSOAPEnvelope , Message.getSOAPHead ,Message.getSOAPBody 等多个方法对 Web 服务的回发信息进行监测。若登录成功,系统检测到 LoginReturn 值为 true 时,则修改成功登录人数数量。
除此以外,开发人员还可在 SOAP 的 head、body 等多处地方额外添加回发信息。下面的例子将记录成功登陆的人数,并把此信息记录在头文件中返还到客户端。
1 public interface ILoginService { 2 public Boolean login(String name,String password); 3 } 4 5 public class LoginServiceImpl implements ILoginService { 6 7 @Override 8 public Boolean login(String name, String password) { 9 UserService userService=new UserService(); 10 User user=userSerivice.getUser(name); 11 if(user!=null) 12 return user.password==password; 13 else 14 return false; 15 } 16 } 17 18 public class LoginHandler extends BasicHandler{ 19 private MessageContext context; 20 21 public void invoke(MessageContext context){ 22 this.context=context; 23 try { 24 if(success()) 25 logged(); 26 addElement(); 27 getSOAP(); 28 } catch (AxisFault e) { 29 e.printStackTrace(); 30 } catch (SOAPException e) { 31 e.printStackTrace(); 32 } 33 } 34 35 //记录登录人数 36 private void logged(){ 37 Handler handler=context.getService(); 38 //判断服务是否为LoginService 39 if(handler.getName().equals("LoginService")){ 40 if(this.getOption("loggedCount")==null) 41 this.setOption("loggedCount", 0); 42 43 Integer count=Integer.parseInt( 44 this.getOption("loggedCount").toString()); 45 count++; 46 this.setOption("loggedCount", count); 47 48 System.out.println("logged count: "+count+" "+ 49 new Date().toString()+"\n"); 50 } 51 } 52 53 //获取LoginService服务返回值 54 //若登录成功则返回true,失败则返回false 55 private Boolean success() throws SOAPException{ 56 SOAPBody soap=context.getCurrentMessage().getSOAPBody(); 57 Node node=soap.getElementsByTagName("LoginReturn").item(0); 58 return node.toString().contains("true"); 59 } 60 61 //在回发的SOAP中加入已登录人数的信息 62 private void addElement() throws SOAPException { 63 SOAPEnvelope soap=context.getCurrentMessage().getSOAPEnvelope(); 64 SOAPElement element=soap.getHeader().addChildElement("loggedOnline"); 65 element.addTextNode(this.getOption("loggedCount").toString()); 66 } 67 68 //显示SOAP信息 69 private void getSOAP() throws AxisFault{ 70 SOAPEnvelope soap=context.getCurrentMessage().getSOAPEnvelope(); 71 if(soap!=null){ 72 String[] data= soap.toString().split(">"); 73 for(String line:data) 74 System.out.println(line+">"); 75 } 76 } 77 }
客户端
1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3 LoginServiceImpl service1=getService(); 4 service1.login("Leslie", "12345678"); 5 } 6 7 private static LoginServiceImpl getService() throws 8 AxisFault, MalformedURLException{ 9 LoginServiceImplServiceLocator locator=new LoginServiceImplServiceLocator(); 10 locator.setMaintainSession(true); 11 LoginServiceImpl loginService=new LoginServiceSoapBindingStub( 12 new URL("http://localhost:8080/axis.server/services/LoginService"), 13 locator); 14 return loginService; 15 }
运行结果
回到目录
五、新一代 SOAP 引擎 Axis 2.x
5.1 Axis 2.x 核心结构
Axis 1.x 建立在 JAX-RPC 基础之上的, 但事实证明这并非一个好方法,因为 JAX-RPC 限制了 Axis 代码的功能,而且造成了性能问题使系统缺乏灵活性。Axis 2.x 在设计时已经考虑到灵活性操作的问题,它同时实现了对 JAXB 2.x、 Java XML 数据绑定标准,并以 JAX-WS 技术替代了 JAVA-PRC 作为 Java Web 服务标准。
Axis 2.x 是纯 SOAP 处理引擎,它的核心功能是处理传输消息,并将其交付给目标应用程序。像 JAX-WS 此类 Web 服务标准不会进入 Axis 2.x 核心部分当中,而只作为 Axis 2.x 服务传递组件。 AXIOM(Axis2 Object Model,Axis 2 对象模型)才是 Axis2 的基础,任何 SOAP 消息在 Axis2 中都可看作为 AXIOM。它把延迟构建和轻型的可定制对象模型结合了起来,尽可能地减轻对系统资源特别是 CPU 和内存的压力。
关于 Axis 2.x 的消息处理过程与 AXIOM 对象模型将在下节再作进一步介绍。
5.2 Axis 2.x 安装部署
Axis 2.x 可以在 http://axis.apache.org/axis2/java/core/index.html 下载,当中包括Binary Distribution、WAR 等多个版本,使用WAR 版本更方便把 Web 服务部署到 Tomcat、WebLogic 等服务管理器上,在开发阶段,使用 Binary Dirstribution 等版本更便于服务的调试。
完成下载后在环境变量中把 Axis2_Home 绑定到 Axis 2.x 的根目录,在 path 加入设置 ".;%Axis2_Home%\bin "。
Axis 1.x 当中只是包括了几个工具包,而 Axis 2.x 更像是一个框架,在 Axis 2.x 项目的“\WebRoot\WEB-INF\” 文件夹内包含了 Axis 2.x 多个储存库,在 “ conf ” 文件夹内的 “ axis2.xml ” 文件是 Axis 2.x 全局描述符,所有的系统级配置都是通过 “ axis2.xml ” 文件完成的。在 “ services” 文件夹是用于存放后缀名为 “ *.aar ” 的服务模块的,在 “ modules ” 文件夹内用于存放后缀名为 “ *.mar ” 的自定义模块的,在 “ pojo ” 文件夹内用于存放传统的 POJO 对象服务文件。 而服务描述符 “services.xml” 文件 与模块描述符 “module.xml”文件则存放于“ \WebRoot\META-INF ” 文件夹当中。
5.3 将传统的 POJO 对象作为服务对象部署
Axis 2.x 为用户提供了最简约的服务部署方式,能把简单的一个 POJO 对象作为服务发布。
首先在一个缺省包里建立一个 POJO 对象,再把被编译后的 Example.class 文件加入到 “\WebRoot\WEB-INF\pojo\” 文件夹内,此时 Example 即会被默认为POJO 服务。
1 public class Example { 2 public String HelloWorld(String name){ 3 return "Hello "+name; 4 } 5 }
运行程序后,你就可以 http://leslie-laptop:8080/axis2-1.6.2/services/Example?wsdl 上看到 Example 服务的 wsdl 信息。
5.4 以存档文件部署服务
使用 POJO 对象部署服务固然简单,但由于在安全性事务、消息监听等方面缺乏支持,所以 Axis 2.x 更多时候是使用存档文件方式部署服务的。首先在项目内建立服务接口 PersonService 和 服务类PersonServiceImpl。
1 public class PersonEntity implements Serializable { 2 private Integer id; 3 private String name; 4 private Integer age; 5 private String address; 6 7 public PersonEntity(Integer id,String name,Integer age,String address){ 8 this.id=id; 9 this.name=name; 10 this.age=age; 11 this.address=address; 12 } 13 14 public Integer getId(){ 15 return id; 16 } 17 18 public void setId(Integer id){ 19 this.id=id; 20 } 21 ........ 22 } 23 24 public interface PersonService { 25 PersonEntity getPerson(int id); 26 PersonEntity[] getList(); 27 ListgetListByName(String name); 28 } 29 30 public class PersonServiceImpl implements PersonService { 31 32 @Override 33 public PersonEntity[] getList(){ 34 PersonEntity[] list=new PersonEntity[2]; 35 PersonEntity person1=new PersonEntity(1,"Leslie",32,"tianhe"); 36 PersonEntity person2=new PersonEntity(2,"Elva",31,"henan"); 37 list[0]=person1; 38 list[1]=person2; 39 return list; 40 } 41 42 @Override 43 public List getListByName(String name){ 44 List list=new LinkedList (); 45 PersonEntity person1=new PersonEntity(1,name+" Lee",32,"tianhe"); 46 PersonEntity person2=new PersonEntity(2,name+" Chen",31,"henan"); 47 list.add(person1); 48 list.add(person2); 49 return list; 50 } 51 52 @Override 53 public PersonEntity getPerson(int id){ 54 return new PersonEntity(id,"Leslie",32,"tianhe"); 55 } 56 }
在 “ \WebRoot\META-INF ” 文件夹内加入配置文件 services.xml 。当中 ServiceClass 的 parameter 用于绑定服务实现类,而 operation 用于绑定要暴露的服务方法。
Axis 2.x 支持三种信息交换模式,包括 In-Only,Robust-In,In-Out 。In-Only 消息交换模式只接收 SOAP 请求,而无需返还信息;Robust-In 消息交换模式发送SOAP 请求,只有在出错的情况下才返回应答; In-Out 消息交换模式总是对 SOAP 请求返还信息。在服务的 messageReceive 设置中有 RPCMessageReceiver、RawXMLINOutMessageReceiver、RawXMLINOnlyMessageReceiver 等多个选项可以针对不同 Web 服务方法设置不同的信息交换模式。
1 <service name="PersonService"> 2 <description>This is a sample Web Service.description> 3 4 <parameter name="ServiceClass" locked="false"> 5 axis2.serviceImpl.PersonServiceImpl 6 parameter> 7 8 <operation name="getPerson"> 9 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 10 operation> 11 <operation name="getList"> 12 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 13 operation> 14 <operation name="getListByName"> 15 <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> 16 operation> 17 service>
完成配置后,把 “META-INF\services.xml ” 文件(包含文件夹 META-INF)和服务类 PersonEntity.class、PersonService.class、PersonServiceImpl 复制到自定义文件夹 “axis2serivces” 当中。打开命令提示符,进入 axix2services 文件夹输入命令 “ jar cvf axisService.aar . ” (注意:“.” 代表生成包含文件夹所有文件)。最后把 “ axisService.aar ” 文件加入到 “\WebRoot\WEB-INF\services” 文件夹当中,启动 Axis2.x 项目,打开 http://leslie-laptop:8080/axis2-1.6.2/services/PersonService?wsdl 就可看到 PersonService 服务的 wsdl 信息。在 wsdl 中可以看到 axis2 支持 SOAP 1.1、 SOAP 1.2 等多种传输格式。
Axis 2.x 支持多种客户端生成工具,包括原有 WSDL2Java 工具
WSDL2Java -uri http://localhost/axis2-1.6.2/services/PersonSerivce.wsdl -p 包名 -o 文件夹
还有支持 JAX-WS 的 WsImport 工具
Wsimport -p 包名 -keep -extension http://localhost/axis2-1.6.2/services/PersonSerivce.wsdl
也可使用 MyEclipse 自带的 JAX-WS 客户端生成工具完成。
生成客户端后可以进行测试
1 public static void main(String[] args) throws MalformedURLException { 2 // TODO Auto-generated method stub 3 getList(); 4 } 5 6 private static void getList() throws MalformedURLException{ 7 PersonService personService=new PersonService(); 8 PersonServicePortType personServicePortType=personService 9 .getPersonServiceHttpSoap12Endpoint(); 10 ListpersonList=personServicePortType.getList(); 11 for(PersonEntity person:personList) 12 displayPersonProperty(person); 13 } 14 15 private static void displayPersonProperty(PersonEntity person){ 16 System.out.println("Id:"+person.getId().getValue()+" Name:" 17 +person.getName().getValue()+" Age:"+ person.getAge().getValue() 18 +" Address:"+person.getAddress().getValue()); 19 }
测试结果
回到目录
六、AXIOM 对象模型
6.1 AXIOM 的特点
AXIOM(Axis Object Model, Axis 对象模型)是 Axis 2.x 对 XML 信息处理的核心部分,它把延迟构建和可定制对象模型技术结合起来,极大地提高了 SOAP 信息构建的灵活度。对应 Axis 1.x 的 SAX ( Simple API for XML)推式(Push)解析器,Axis 2.x 使用更具灵活性的 StAX(Streaming API for XML ) 拉式(Pull)解析器,可尽量减轻对系统资源的压力。在使用推方式(Push)的情况下,系统会先定义数据的处理程序,然后在数据录入时对处理程序进行回调。然而回调操作只能对录入的数据进行如读取、修改等某些操作,除非引发异常,否则无法左右 XML 文档的录入。而 AXIOM 所使用的拉式(Pull)解析器,实际上是一个高效的迭代器,它使用 XML 树形结构方式,支持延时构建,可以根据需要对文档中的不同部分进行遍历。在大型的 XML 文件中,使用拉式解析器更具吸引力,它可以仅对部分 XML 数据进行处理,剩下的留给解析器完成操作。
在 Web服务开发过程中,大部分的开发人员都是以对象的形式进行信息传递,并使用 WSDL2Java 等工具构建服务代理以实现 XML 数据与对象之间的转换。然而使用此方式限制了Web 服务框架中的数据绑定的灵活程度,利用 AXIOM 的特性更便于 XML 与 Java 对象之间转换,大部分的 JAVA 对象都可以利用 AXIOM 转换成 SOAP 信息。
6.2 AXIOM 的使用方式
AXIOM 建立于 StAX 拉式解析器的基础上,它为开发人员准备了完善的 API ,其中最常用到的是 OMFactory 工厂,它提供了createOMNamespace、createOMElement、createOMAttribute、createOMDocument、createOMText 等多个方法用于建立 XML 文档信息。在 BeanUtil 类中还包括了 getPullParser、getOMElement、processObject 等多个静态方法用于处理自定义对象与XML之间的转换。
下面以POCO服务作为一个例子,演示一下 AXIOM 对象绑定方式。在客户端 Web服务分为 SOAP 请求数据绑定与返回信息处理两个阶段,在服务请求阶段系统会把设定的服务地址、传输方式绑定到 ServiceClient 请求对象当中,然后利用 serviceClient.sendReceive 的方法把已定义的 OMElement 对象信息加入到 SOAP 当中发送到服务端。当接收到返回信息后,再把 OMElement 信息转换为对象显示。
1 public static void main(String[] args) throws AxisFault { 2 excute(); 3 } 4 5 public static void excute() throws AxisFault{ 6 //设置 endpoint 地址 7 EndpointReference targetEndpoint=new EndpointReference( 8 "http://leslie-laptop:8080/axis2-1.6.2/services/PersonService"); 9 Options options=new Options(); 10 options.setTo(targetEndpoint); 11 12 //设置传输方式 13 //可使用 TRANSPORT_JMS;TRANSPORT_HTTP;TRANSPORT_MAIL;TRANSPORT_TCP; 14 options.setTransportInProtocol(Constants.TRANSPORT_HTTP); 15 16 //把设置的地址、传输方式绑定到Service请求当中 17 ServiceClient sender=new ServiceClient(); 18 sender.setOptions(options); 19 20 //设置请求的 SOAP 信息 21 OMElement requestOMElement=getPersonRequest(1); 22 OMElement responseOMElement=sender.sendReceive(requestOMElement); 23 24 //对返回的SOAP进行处理,显示返回值 25 PersonEntity person=convertToPerson(responseOMElement); 26 displayPersonProperty(person); 27 } 28 29 private static OMElement getPersonRequest(Integer id){ 30 //新建OMFactory工厂 31 OMFactory factory=OMAbstractFactory.getOMFactory(); 32 33 //加入OMNamespace、OMElement、OMText 等数据 34 OMNamespace omNs=factory.createOMNamespace( 35 "http://ws.apache.org/axis2","myNS"); 36 OMElement value=factory.createOMElement("id",omNs); 37 value.addChild(factory.createOMText(value,id.toString())); 38 39 //加入请求方法名 40 OMElement method=factory.createOMElement("getPerson",omNs); 41 method.addChild(value); 42 return method; 43 } 44 45 //把返回OMElement对象转换成 person对象 46 private static PersonEntity convertToPerson(OMElement element) 47 throws AxisFault{ 48 PersonEntity person =null; 49 OMElement omElement = element.getFirstElement().getFirstElement(); 50 String localName=omElement.getLocalName().toLowerCase(); 51 if (localName.equals("personentity")) { 52 person = (PersonEntity) BeanUtil.processObject(omElement, 53 PersonEntity.class, null, true, new DefaultObjectSupplier(), null); 54 } 55 return person; 56 } 57 58 private static void displayPersonProperty(PersonEntity person){ 59 System.out.println("Id:"+person.getId()+" Name:"+person.getName() 60 +" Age:"+ person.getAge() +" Address:"+person.getAddress()); 61 }
发送的 SOAP 请求
在服务端,系统会从发送的SOAP信息中获取请求的 id 值,并通过 BeanUtil.getPullParser 等方法把 personEntity 对象转换成 SOAP 信息返还到客户端。
1 public class PersonService { 2 3 public OMElement getPerson(OMElement element){ 4 //获取请求条件 Id 5 Integer id=Integer.valueOf(element.getText()); 6 //模拟返回数据 7 PersonEntity person=new PersonEntity(id,"Leslie",32,"tianhe"); 8 //把person对象转换为OMElement 9 javax.xml.stream.XMLStreamReader reader=BeanUtil.getPullParser(person); 10 StreamWrapper parser=new StreamWrapper(reader); 11 OMXMLParserWrapper stAXOMBuilder=OMXMLBuilderFactory 12 .createStAXOMBuilder(OMAbstractFactory.getOMFactory(), parser); 13 return stAXOMBuilder.getDocumentElement(); 14 } 15 }
返还的 SOAP 信息
6.3 突显 AXIOM 的优势
在面向对象的 Web服务开发模式下,系统都会利用集成工具进行XML信息与对象的自动化转换。数据的搜索与查找都会在JAVA对象中进行,在信息交换密度频繁的系统当中,这将占用大量内存空间,对系统造成压力。所以,在返回信息中直接对 XML 数据进行拦截、分类、筛选是常用的方法。Axis 2.x 使用更具灵活性的 StAX 拉式解析器,它可以使用虚拟文档的方式构建 XML 树。每个节点都可被视为一个容器 OMContainer,它可以使用 OMContainer
下面是服务端的代码:.getChildren 方法获取子节点,再以
Iterator 迭代器的方式对节点进行遍历。此外系统还提供了
OMNode.getNextOMSibling、
OMNode.getPreviousOMSibling 等多个
方法,以进行节点之间的跳转。这意味着它可以跳过其他的子节点,直接找到需要的节点再进行遍历。在数据量较大的系统当中使用此种遍历方式更能突显出 StAX 拉式(Pull)解析器的优势。
以下的例子主要为了展示使用 StAX 拉式解析器进行遍历的方式,BookService 服务主要是根据客户所输入的出版社信息进行查找,然后把
,返还到客户端。该出版社的书本进行按类分配
1 public class BookEntity { 2 private Integer id; 3 private String title; 4 private String author; 5 private String publishing; 6 private String introduction; 7 private String type; 8 9 public BookEntity(Integer id,String title,String author 10 ,String type,String publishing,String introduction){ 11 this.id=id; 12 this.title=title; 13 this.author=author; 14 this.type=type; 15 this.publishing=publishing; 16 this.introduction=introduction; 17 } 18 19 public Integer getId(){ 20 return id; 21 } 22 23 public void setId(Integer id){ 24 this.id=id; 25 } 26 ........ 27 } 28 29 public interface BookService { 30 OMElement getList(OMElement element); 31 } 32 33 public class BookServiceImpl implements BookService{ 34 35 @Override 36 public OMElement getList(OMElement element) { 37 //获取请求信息 publishing 出版社名称 38 OMElement child=(OMElement)element.getChildren().next(); 39 String publishing=child.getText(); 40 41 // 构建 OMFactory 工厂 42 OMFactory factory=OMAbstractFactory.getOMFactory(); 43 OMNamespace omNamespace=factory.createOMNamespace( 44 "http://serviceImpl.axis2","ns"); 45 46 //获取计算机类书本子节computerElement 47 ListcomputerTypeList=getBooks( 48 publishing,"computer"); 49 OMElement computerElement=convertToOMElement( 50 computerTypeList,"computer",omNamespace); 51 52 //获取文学类书本子节literatureElement 53 List literatureTypeList=getBooks( 54 publishing,"literature"); 55 OMElement literatureElement=convertToOMElement( 56 literatureTypeList,"literature",omNamespace); 57 //加入多个类型的书本 58 ........... 59 //构建XML树 60 OMElement response=factory.createOMElement( 61 "getListResponse",omNamespace); 62 OMElement returnValue=factory.createOMElement 63 ("return",omNamespace); 64 OMElement books=factory.createOMElement( 65 "publishing",omNamespace); 66 67 books.addAttribute("name", publishing, omNamespace); 68 books.addChild(computerElement); 69 books.addChild(literatureElement); 70 ......... 71 returnValue.addChild(books); 72 response.addChild(returnValue); 73 return response; 74 } 75 76 //把计算机类书本对象转换为XML信息 77 private OMElement convertToOMElement(List list 78 ,String typeName,OMNamespace omNamespace){ 79 OMElement omElement=BeanUtil.getOMElement(new QName("theme") 80 ,list.toArray(),new QName("book"),false,null); 81 omElement.addAttribute("name",typeName,omNamespace); 82 return omElement; 83 } 84 85 //根据书本类型查找数据 86 private List getBooks(String publishing,String type){ 87 List list=new ArrayList (); 88 for(BookEntity book:virtualDatabase(publishing)) 89 if(book.getType()=="computer") 90 list.add(book); 91 return list; 92 } 93 94 //虚拟数据 95 private List virtualDatabase(String publishing){ 96 List list=new ArrayList (); 97 BookEntity book1=new BookEntity( 98 1,"Core JAVA Advanced Features","Gary Cornell", 99 "computer",publishing, 100 "Core Java by Cay S. Horstmann and Gary Cornell is a book\n"+ 101 "in the Java series of Sun Microsystems Press, published \n"+ 102 "by Prentice-Hall. The book is aimed at experienced \n"+ 103 "programmers who want to learn how to write useful Java \n" + 104 "applications and applets. No hype, no toy code, no language \n"+ 105 "lawyering, just solid facts and in-depth research to help you \n"+ 106 "write real programs." 107 ); 108 list.add(book1); 109 ........ 110 return list; 111 } 112 }
在配置services.xml文件时,需要把 messageReceiver 设置为 org.apache.axis2.receivers.RawXMLINOutMessageReceiver
1 <service name="BookService"> 2 <description>This is a sample Web Service.description> 3 4 <parameter name="ServiceClass" locked="false">axis2.serviceImpl.BookServiceImplparameter> 5 6 <operation name="getList"> 7 <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> 8 operation> 9 service>
返还的 SOAP 信息
在客户端利用 StAX 就可以轻松地对返还数据进行分类处理,例如要在返回数据当中显示计算机类型的Book信息,可以使用 Iterator 方式进行遍历,利用 StaX 推时分析的特点跳过其他类型的节点,直至遇到 ns:name=computer 的节点时,才把该节点的XML树放入容器进行处理。使用此种遍历方式,所占用的内存空间更小,在返回数据量较大的系统当中更能突显其优势。
1 public static void main(String[] args) throws AxisFault { 2 //设置请求的endpoint地址 3 EndpointReference targetEndpoint=new EndpointReference( 4 "http://leslie-laptop:8080/axis2-1.6.2/services/BookService"); 5 Options options=new Options(); 6 options.setTo(targetEndpoint); 7 8 //设置传输方式 9 //可使用 TRANSPORT_JMS;TRANSPORT_HTTP;TRANSPORT_MAIL;TRANSPORT_TCP; 10 options.setTransportInProtocol(Constants.TRANSPORT_HTTP); 11 12 //把设置的地址、传输方式绑定到Service请求当中 13 ServiceClient sender=new ServiceClient(); 14 sender.setOptions(options); 15 16 //设置请求的 SOAP 信息,输入出版社名称 17 OMElement requestOMElement=getBookRequest("China Machine Press"); 18 //发送请求 19 OMElement responseOMElement=sender.sendReceive(requestOMElement); 20 //遍历返回值,在返回信息中获取computer类的Element 21 OMElement computerBookElement=getComputerElement(responseOMElement); 22 //把XML转换为BookEntity对象集 23 Listlist=convertToBooks(computerBookElement); 24 for(BookEntity book:list) 25 DisplayBook(book); 26 } 27 28 //把XML数据转换为BookEntity对象集 29 private static List convertToBooks(OMElement element) 30 throws AxisFault{ 31 List list=new ArrayList (); 32 Iterator books=element.getChildElements(); 33 while(books.hasNext()){ 34 OMElement bookElement=(OMElement)books.next(); 35 BookEntity book=(BookEntity) BeanUtil.processObject(bookElement, 36 BookEntity.class, null, true, new DefaultObjectSupplier(), null); 37 list.add(book); 38 } 39 return list; 40 } 41 42 //对OMElement元素进行遍历,找到computer类型的节点 43 private static OMElement getComputerElement(OMElement element){ 44 OMElement returnOMElement=(OMElement)element 45 .getChildElements().next(); 46 OMElement publishingOMElement=(OMElement)returnOMElement 47 .getChildElements().next(); 48 Iterator themes=publishingOMElement.getChildElements(); 49 OMElement theme=null; 50 while(themes.hasNext()){ 51 theme=(OMElement)themes.next(); 52 OMAttribute themeName=(OMAttribute)theme.getAllAttributes().next(); 53 if(themeName.getAttributeValue().equals("computer")) 54 break; 55 } 56 return theme; 57 } 58 59 //构建请求文档 60 private static OMElement getBookRequest(String publishing){ 61 //新建OMFactory工厂 62 OMFactory factory=OMAbstractFactory.getOMFactory(); 63 64 //绑定OMNamespace,请求数据 65 OMNamespace omNamespace=factory.createOMNamespace( 66 "http://serviceImpl.axis2","ns"); 67 OMElement value=factory.createOMElement("publishing",omNamespace); 68 value.addChild(factory.createOMText(value,publishing.toString())); 69 70 //绑定请求方法名 71 OMElement method=factory.createOMElement("getList",omNamespace); 72 method.addChild(value); 73 return method; 74 } 75 76 private static void DisplayBook(BookEntity book){ 77 System.out.println("##Id##:"+book.getId()+" ##Title##:"+book.getTitle() 78 +"\n##Author##:"+book.getAuthor()+" ##Type##:"+book.getType() 79 +" ##Publishing##:"+book.getPublishing()+"\n##Introduction##:\n" 80 +book.getIntroduction()+"\n"); 81 }
发送的SOAP请求
显示结果
回到目录
七、Module 模块独立化处理方式
7.1 Axis 2.x 信息处理流程
Axis 2.x 对信息处理流程作出了较大幅的修改,在介绍模块处理结构前,需要简单介绍一下 Axis 2.x 的信息处理机制。Axis 1.x 只接受请求-响应的信息处理模式,而 Axis 2.x 支持 In-Only、In-Out 和 Robust-In 三种消息交换模式。而这三种消息交换机制是建立在 TransportListener 和 TransportSender 之上的,在 SOAP 信息进站时,系统会通过 TransportListener 进行监听。通过一系列处理后,最后会由 TransportSender 进行信息的回送。
系统定义了 InFlow、OutFlow 两种流用于处理服务器端的请求消息和响应消息。而 InFaultFlow、OutFaultFlow 只会在请求或者响应出现错误时才会被调用。当TransportListener 监听到入站信息时会把信息送到 InFlow 流当中,系统可通过修改 “ \WEB-INF\conf\axis2.xml” 配置文件,把多个 Phase 绑定到 InFlow 当中。每个 Phase 相当于一个阶段,同一个阶段可以绑定多个 Handler 进行处理。数据通过 InFlow 流处理后就会被发送到 MessageReceiver,在 services.xml 中会绑定对应的 MessageReceiver 服务方法。最后通过 OutFlow 流对返回信息进行处理后,由 TransportSender 把 SOAP 响应回发到客户端。
7.2 Module 模块结构
Axis 2.x 把 Handler 放入Module 模块当中,进行了独立化处理。每个 Module 模块是一个容器,当中可以包含多个 Handler 处理程序、第三方库、模块相关资源和模块配置文件。系统把Module 模块定义为后缀名为 “ *.mar ” 的文件,系统可通过 “ jar cvf myModule.mar . ” 命令可以生成 *.mar 的模板文件。当中必须包含 module.xml 文件对 Handler 进行部署,否则系统会无法识别此模块。想要在某个 Web服务的 InFlow 流或 OutFlow 流中调用 Module 模块中的 Handler,还需要修改 axis2.xml 文件,在phaseOrder 中加入自定义的 phase, 并绑定 Handler 的处理类。最后修改 “ services.xml ” 文件,在对应此服务配置中加入
下面先以一个简单的例子,说明 Module 模块的使用方式。在例子中将建立一个 MyModule 模块,在模块中加入一个 InputHandler 处理文件对 InFlow 流进行检测。首先建立 org.apache.axis2.modules.Module 的子类 MyModule,此类是用于对模块init 、engageNotify 等事件进行监测的。然后建立 InputHandler 类,此类必须继承 org.apache.axis2.engine.Handler 且实现 org.apache.axis2.engine.Handler.AbstractHandler 接口的 public InvocationResponse invoke(MessageContext context) 方法,此方法的返回值 InvocationResponse 包括 CONTINUE,SUSPEND,ABORT 三个选项,可以"继续"或者"停止"流的执行。
在流输入时此方法将会被自动执行,它所带的 MessageContext 参数对象中将包括此服务的上下文信息。
此例子的主要目的是为开发人员显示一下在 InFlow 流可以获取到的数据信息,所以在此先介绍一下 MessageContext 常用方法:
方法 | 说明 |
getCurrentMessageContext() | 获取当前上下文对象 |
getExecutedPhases() | 获取包含在此流中的 Phase 集合 |
getAxisService() | 获取被调用的 AxisService 服务对象 |
getEnvelope() | 获取 SOAP,在 InFlow 中,此方法将显示该请求的 SOAP 信息 |
getFrom() | 获取客户端地址 |
getTo() | 获取服务地址 |
MessageContext 常用方法
当中 getAxisService 方法所返回的 AxisService 对象正是当前被调用的 Web服务对象。
方法 | 说明 |
getOperationContext() | 返回当前被调用的 OperationContext 上下文对象 |
getOperations() | 返回此服务所包含的所有 Operation 对象 |
getParameters() | 返回此服务的所包含的所有 Parameters 对象 |
getFileName() | 返回此*.aar 服务文件路径 |
getTargetNamespace() | 返回此服务的 targetNamespace 名称 |
getEndpointName() | 返回此服务的 Endpoint 名称 |
AxisService 常用方法
1 public class MyModule implements Module{ 2 3 @Override 4 public void applyPolicy(Policy arg0, AxisDescription arg1) 5 throws AxisFault { 6 // TODO Auto-generated method stub 7 } 8 9 @Override 10 public boolean canSupportAssertion(Assertion arg0) { 11 // TODO Auto-generated method stub 12 return false; 13 } 14 15 @Override 16 public void engageNotify(AxisDescription arg0) throws AxisFault { 17 // TODO Auto-generated method stub 18 } 19 20 @Override 21 public void init(ConfigurationContext arg0, AxisModule arg1) 22 throws AxisFault { 23 // TODO Auto-generated method stub 24 } 25 26 @Override 27 public void shutdown(ConfigurationContext arg0) throws AxisFault { 28 // TODO Auto-generated method stub 29 } 30 } 31 32 public class InputHandler 33 extends AbstractHandler implements Handler{ 34 35 public InvocationResponse invoke(MessageContext context){ 36 //显示当前上下文信息 37 System.out.println("##from:##\n "+context.getFrom() 38 +"\n##to:##\n "+context.getTo()); 39 //显示所执行的phase信息 40 phasesMessageDisplay(context.getExecutedPhases()); 41 //显示服务对象信息 42 AxisService axisService=context.getAxisService(); 43 serviceMessageDisplay(axisService); 44 //显示当前operation信息 45 AxisOperation operation=context.getOperationContext().getAxisOperation(); 46 currentOperationDisplay(operation); 47 //显示服务的所有operation信息 48 operationsMessageDisplay(axisService.getOperations()); 49 //显示服务参数parameters信息 50 parametersMessageDisplay(axisService.getParameters()); 51 return InvocationResponse.CONTINUE; 52 } 53 54 //显示所执行的phases 55 private void phasesMessageDisplay(Iteratoriterator){ 56 String data="##phaseList:##\n"; 57 while(iterator.hasNext()){ 58 Handler handler=(Handler)iterator.next(); 59 data+=" "+handler.getName(); 60 } 61 System.out.println(data); 62 } 63 64 //显示服务信息 65 private void serviceMessageDisplay(AxisService axisService){ 66 System.out.println("\n##serviceFile:##\n "+axisService.getFileName()+ 67 "\n##targetNamespace:##\n "+axisService.getTargetNamespace()+ 68 "\n##endpointName##:\n "+axisService.getEndpointName()+"\n"); 69 } 70 71 //显示当前Operation信息 72 private void currentOperationDisplay(AxisOperation operation){ 73 System.out.println("##currentOperation:##\n name:" 74 +operation.getName().getLocalPart()+"\n messageReceive:" 75 +operation.getMessageReceiver().toString()+"\n"); 76 } 77 78 //显示服务的所有Operation信息 79 private void operationsMessageDisplay(Iterator operations){ 80 while(operations.hasNext()){ 81 AxisOperation operation=(AxisOperation)operations.next(); 82 System.out.println("##operation:##\n name:" 83 +operation.getName().getLocalPart()+"\n messageReceive:" 84 +operation.getMessageReceiver().toString()); 85 } 86 System.out.println(); 87 } 88 89 //显示parameter信息 90 private void parametersMessageDisplay(List parameters){ 91 for(Parameter parameter:parameters){ 92 System.out.println("##parameter:##\n name:"+parameter.getName() 93 +"\n value:"+parameter.getValue().toString()); 94 } 95 } 96 }
在 “ \WebRoot\META-INF\ ” 内加入 “ module.xml ” 配置文件
1 <module name="myModule" class="axis2.module.MyModule"> 2 <InFlow> 3 <handler name="inputHandler" class="axis2.handler.InputHandler"> 4 <order phase="orderInPhase"/> 5 handler> 6 InFlow> 7 module>
把 “module.xml ” 配置文件(包含 “ META-INF ”文件夹)和编译后的 MyModule.class、InputHandler.class 加入到自定义文件夹,使用命令提示符进行入自定义文件夹内输入 “ jar cvf myModule.mar . ” 命令生成 “ myModule.mar ” 包,把生成的包加入 “\WEB-INF\modules ” 文件夹内。
最后修改 “ \WEB-INF\conf\axis2.xml” 配置文件,加入自定义的 phase 配置。此时重启系统,即完成了 myModule.mar 自定义模块的配置。
注意:自定义 handler 可以在 axis2.xml 或者 module.xml 中进行绑定。
axis2.xml 中所配置的是全局变量,如果直接在 axis2.xml 文件中加入对 handler 进行绑定,那所有的服务在 InFlow 和 OutFlow 中都执行此 handler。
如果只在 axis2.xml 中建立自定义 phase,然后在 module.xml 中绑定 handler,那只有在服务绑定此 module 时,该 handler 才会被执行 。配置应该按需要而定,一般只有登录、日志记录、系统信息监视等 handler 才会在 axis2.xml 中直接绑定。
1 <phaseOrder type="InFlow"> 2 <phase name="orderInPhase"> 3 <handler name="inputHandler" class="axis2.handler.InputHandler"/> 4 phase> 5 phaseOrder>
想要在某 Web 服务中调用此模块,只需要修改对应的 services.xml 配置文件,在服务配置内加入“
1 <service name="PersonService"> 2 <description>This is a sample Web Service.description> 3 4 <module ref="myModule"/> 5 6 <parameter name="ServiceClass" locked="false">axis2.serviceImpl.PersonServiceImplparameter> 7 8 <operation name="GetPerson"> 9 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 10 operation> 11 <operation name="GetList"> 12 <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> 13 operation> 14 <operation name="GetListByName"> 15 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 16 operation> 17 service>
在启动此Web服务,调用 GetPerson 服务方法时,InputHandler 将会对服务流进行处理,显示测试结果:
7.3 Module 实用方式
以上对 Module 的使用方式进行了简单的介绍,下面想以一个更为实际的订单管理例子介绍一下 Module 的用途。在Web服务项目当中,很多的服务在操作前都需要先进行登录验证,所以下面例子当中会把用户登录功能独立开来,作为一个自定义 Handler 置于模块当中,这样可以使登录服务的功能更为独立,易于管理。用户在调用 OrderService 服务时,会先在头文件中加入用户信息, 在服务端接收到 SOAP 请求时,在 InFlow 流中加入 OrderInputHandler 进行处理,获取头文件信息进行登录,登录成功就会在 Session 中记录 User 对象信息。
像 Order 管理这类服务中,订单费用的计算往往是一个长期不变的规则,但销售商很多时候会进行产品的抽奖,优惠等销售策略,这些策略都是短期的,具有多变性。如果直接把这些业务规则写入 OrderService 服务中,OderService 就会经常需要修改。此时,可以尝试利用 OutFlow 流 ,建立 OrderOutputHandler 对完成操作的 Order 进行检测。若通过得奖规则,则在SOAP返还信息的头文件中加入奖励信息。
由于本节的目的主要是为了展示 Module 的独立性与灵活性,所以省略了 OrderManager、UserManager、FavourableManager 等操作对象。
首先建立 OrderInputHandler 在 SOAP 信息进入 InFlow 流时进行登录处理。
然后建立 OrderOutputHandler 在 SOAP 信息进行 OutFlow 时对已处理Order对象进行检测。
1 public class MyModule implements Module{ 2 3 @Override 4 public void applyPolicy(Policy arg0, AxisDescription arg1) 5 throws AxisFault { 6 // TODO Auto-generated method stub 7 } 8 9 @Override 10 public boolean canSupportAssertion(Assertion arg0) { 11 // TODO Auto-generated method stub 12 return false; 13 } 14 15 @Override 16 public void engageNotify(AxisDescription arg0) throws AxisFault { 17 // TODO Auto-generated method stub 18 } 19 20 @Override 21 public void init(ConfigurationContext arg0, AxisModule arg1) 22 throws AxisFault { 23 // TODO Auto-generated method stub 24 } 25 26 @Override 27 public void shutdown(ConfigurationContext arg0) throws AxisFault { 28 // TODO Auto-generated method stub 29 } 30 } 31 32 public class OrderInputHandler 33 extends AbstractHandler implements Handler { 34 35 public InvocationResponse invoke(MessageContext context){ 36 //判断被调用的是否OrderSerivce的addOrder方法 37 if(context.getOperationContext().getAxisOperation() 38 .getName().getLocalPart().equals("addOrder")){ 39 //获取头文件信息 40 SOAPHeader head=context.getEnvelope().getHeader(); 41 //把头文件信息转换成User对象进行登录 42 String[] userMes=head.getFirstElement().getText().split(","); 43 //用户登录 44 UserEntity user=UserManager.Login(userMes[0],userMes[1]); 45 ....... 46 if(user!=null){ 47 //登录成功记录User 48 SessionContext session=context.getSessionContext(); 49 if(session.getProperty("User")==null) 50 session.setProperty("User", user); 51 } 52 } 53 return InvocationResponse.CONTINUE; 54 } 55 } 56 57 public class OrderOutputHandler 58 extends AbstractHandler implements Handler{ 59 60 public InvocationResponse invoke(MessageContext context){ 61 //判断被调用的是否OrderSerivce的addOrder方法 62 if(context.getOperationContext().getAxisOperation() 63 .getName().getLocalPart().equals("addOrder")){ 64 //获取返回信息的SOAPBody,判断订单总体价格是否高于 200 dollar 65 SOAPBody body=context.getEnvelope().getBody(); 66 OMElement element=body.getFirstElement(); 67 try { 68 OrderEntity order = (OrderEntity) BeanUtil.processObject(element, 69 OrderEntity.class, null, true, new DefaultObjectSupplier(), null); 70 71 //总体价格高于 200 dollar的订单在头文件中输入获奖信息 72 if(order.getTotalPrice()>200){ 73 //在数据库中记录获奖订单 74 Favourable favourable=FavourableManager.addOrder(order); 75 ....... 76 //修改头文件,加入获奖信息 77 setHead(context.getEnvelope().getHeader(),favourable); 78 } catch (AxisFault e) { 79 e.printStackTrace(); 80 } 81 } 82 return InvocationResponse.CONTINUE; 83 } 84 85 //修改头文件,加入获奖信息 86 private void setHead(SOAPHeader head,favourable){ 87 OMFactory factory=OMAbstractFactory.getOMFactory(); 88 OMNamespace omNamespace=factory.createOMNamespace( 89 "http://serviceImpl.axis2","ns"); 90 OMElement element=factory.createOMElement("favourable",omNamespace); 91 element.setText("Congratulations! Price is higher than 200 dollar ......"); 92 //加入favourable 信息 93 ......... 94 head.addChild(element); 95 } 96 } 97 98 public class OrderEntity implements Serializable { 99 private Integer id; 100 private String orderCode; 101 private Double totalPrice; 102 ......... 103 104 public OrderEntity(Integer id,String orderCode,Double totalPrice,........){ 105 this.id=id; 106 this.orderCode=orderCode; 107 this.totalPrice=totalPrice; 108 ........ 109 } 110 .......... 111 }
完成自定义Handler后,进行 module.xml 配置,分别在 InFlow 流 OutFlow 绑定 OrderInputHandler 和 OrderOutputHandler
1 <module name="myModule" class="axis2.module.MyModule"> 2 <InFlow> 3 <handler name="orderInputHandler" class="axis2.handler.OrderInputHandler"> 4 <order phase="orderInPhase"/> 5 handler> 6 InFlow> 7 <OutFlow> 8 <handler name="orderOutputHandler" class="axis2.handler.OrderOutputHandler"> 9 <order phase="orderOutPhase"/> 10 handler> 11 OutFlow> 12 module>
修改 axis2.xml 文件,建立自定义的 phase ,在 InFlow 流和 OutFlow 流中加入 orderInPhase 和 orderOutPhase 。
1 <phaseOrder type="InFlow"> 2 <phase name="orderInPhase"/> 3 phaseOrder> 4 <phaseOrder type="OutFlow"> 5 6 <phase name="orderOutPhase"/> 7 phaseOrder>
建立 Web 服务OrderService ,当调用 addOrder 前先检测用户是否登录成功,若用户未登录则释放出异常。若登录成功,把输入的Order对象加入数据库,然后把处理后的 Order 转换为OMElement 返还到客户端。
1 public interface OrderService { 2 OMElement addOrder(OMElement element) throws Exception; 3 } 4 5 public class OrderServiceImpl implements OrderService{ 6 7 @Override 8 public OMElement addOrder(OMElement element) throws Exception{ 9 //验证是否登录成功 10 if(isLogged()){ 11 //把订单加入数据库,把更新后的订单返回客户端 12 OrderEntity order = (OrderEntity) BeanUtil.processObject( 13 element.getFirstElement(),OrderEntity.class, 14 null, true, new DefaultObjectSupplier(), null); 15 //加入订单,计算总体价格,Code号码等信息 16 OrderEntity orderRefresh=OrderManager.addOrder(order); 17 ....... 18 //把修改后order对象转换为OMElement 19 XMLStreamReader reader=BeanUtil.getPullParser(orderRefresh); 20 StreamWrapper parser=new StreamWrapper(reader); 21 OMXMLParserWrapper stAXOMBuilder=OMXMLBuilderFactory 22 .createStAXOMBuilder(OMAbstractFactory.getOMFactory(), parser); 23 return stAXOMBuilder.getDocumentElement(); 24 } 25 else 26 throw new Exception(); 27 } 28 29 //验证用户是否已经登录 30 private boolean isLogged(){ 31 MessageContext context=MessageContext.getCurrentMessageContext(); 32 SessionContext session=context.getSessionContext(); 33 return session.getProperty("User")!=null; 34 } 35 }
在 services.xml 文件加入 module 节点
1 <service name="OrderService"> 2 <description>This is a sample Web Service.description> 3 <module ref="myModule"/> 4 5 <parameter name="ServiceClass" locked="false">axis2.serviceImpl.OrderServiceImplparameter> 6 7 <operation name="addOrder"> 8 <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/> 9 operation> 10 service>
当调用OrderService 服务后,数据进入 OutFlow 流后系统将检测返还数据,若 Order 符合得奖条件,OrderOutputHandler 将在SOAP头部加入得奖信息。下面是 totalPrice 超过 200 dollor (符合得奖条件) 所返回的SOAP信息。
在客户端发送 OrderService.addOrder 请求时,在头文件加入用户userName,password等资料。为了在使用Session存储User对象,需要在客户端使用options.setManageSession(bool)方法打开 sessionContext 。由于使用方式与 Axis1.x 较为相像,在此不再详细说明了。
1 public static void main(String[] args) throws RemoteException, OrderServiceExceptionException { 2 // TODO Auto-generated method stub 3 excute(); 4 } 5 6 public static void excute() throws AxisFault{ 7 EndpointReference targetEndpoint=new EndpointReference( 8 "http://leslie-laptop:8080/axis2-1.6.2/services/OrderService"); 9 Options options=new Options(); 10 options.setManageSession(true); 11 options.setTo(targetEndpoint); 12 13 //设置传输方式 14 //可使用 TRANSPORT_JMS;TRANSPORT_HTTP;TRANSPORT_MAIL;TRANSPORT_TCP; 15 options.setTransportInProtocol(Constants.TRANSPORT_HTTP); 16 17 //把设置的地址、传输方式绑定到Service请求当中 18 ServiceClient sender=new ServiceClient(); 19 sender.setOptions(options); 20 21 //设置请求的 SOAP 信息 22 OMElement requestOMElement=getOrderRequest(new OrderEntity(-1,null,200.5,......)); 23 //在头文件中加入用户资料 24 sender.addHeader(setHead()); 25 OMElement responseOMElement=sender.sendReceive(requestOMElement); 26 27 //对返回的SOAP进行处理,显示返回值 28 OrderEntity order=convertToOrder(responseOMElement); 29 displayOrderProperty(order); 30 } 31 32 //在头文件中加入用户资料 33 private static OMElement setHead(){ 34 //新建OMFactory工厂 35 OMFactory factory=OMAbstractFactory.getOMFactory(); 36 //绑定OMNamespace,请求数据 37 OMNamespace omNamespace=factory.createOMNamespace( 38 "http://serviceImpl.axis2","ns"); 39 //加入用户资料 40 OMElement user=factory.createOMElement("User",omNamespace); 41 user.setText("Leslie,12345678"); 42 return user; 43 } 44 45 private static OMElement getOrderRequest(OrderEntity order){ 46 //新建OMFactory工厂 47 OMFactory factory=OMAbstractFactory.getOMFactory(); 48 //绑定OMNamespace 49 OMNamespace omNamespace=factory.createOMNamespace( 50 "http://serviceImpl.axis2","ns"); 51 //绑定请求方法名 52 OMElement method=factory.createOMElement("addOrder",omNamespace); 53 54 //把order对象转换为OMElement 55 javax.xml.stream.XMLStreamReader reader=BeanUtil.getPullParser(order); 56 StreamWrapper parser=new StreamWrapper(reader); 57 OMXMLParserWrapper stAXOMBuilder=OMXMLBuilderFactory 58 .createStAXOMBuilder(OMAbstractFactory.getOMFactory(), parser); 59 OMElement param=stAXOMBuilder.getDocumentElement(); 60 61 method.addChild(param); 62 return method; 63 } 64 65 //把返回OMElement对象转换成 person对象 66 private static OrderEntity convertToOrder(OMElement element) 67 throws AxisFault{ 68 OrderEntity order =null; 69 OMElement omElement = element.getFirstElement().getFirstElement(); 70 String localName=element.getLocalName().toLowerCase(); 71 if (localName.equals("orderentity")) { 72 order = (OrderEntity) BeanUtil.processObject(element, 73 OrderEntity.class, null, true, new DefaultObjectSupplier(), null); 74 } 75 return order; 76 } 77 //显示Order数据 78 private static void displayOrderProperty(OrderEntity order){ 79 System.out.println("Id:"+order.getId()+" Code:"+order.getOrderCode() 80 +" TotalPrice:"+ order.getTotalPrice()); 81 }
发送的SOAP请求
回到目录
八、异步调用Web服务
在 Axis 1.x 中服务只支持 “请求-回复” 的操作方式,客户端在发送请求后将处于等待的状态,在Web服务操作时间较长的情况下,这种操作方式将影响了系统的效率。从 Axis 2.x 开始客户端支持异步操作的方式,在发送请求后,系统将使用异步线程绑定回调操作。在发出请求后,客户端无需再处理于长期等待的状态。生成异步操作的方法有很多,比较简单的是使用 WSDL2Java 的 “-a” 命令:
WSDL2Java -uri http://localhost/axis2-1.6.2/services/PersonSerivce.wsdl -p 包名 -o 文件夹 -a
所生成的客户端会包含一个 ServiceCallbackHandler 类,下面的例子当中,只要实现了 PersonServiceCallbackHandler 类的 receiveResultgetList 方法,在调用 personServiceStub.startgetList(PersonServiceStub.GetList , PersonServiceCallbackHandler)方法后,系统将释放主线程。当接收到服务端的返还信息后,信息将交由 PersonServiceCallbackHandler 所绑定的方法进行处理。
服务端
1 public class PersonService{ 2 3 public ListgetList(){ 4 List list=new ArrayList (); 5 list.add(new PersonEntity(1,"Leslie",32,"tianhe")); 6 list.add(new PersonEntity(2,"Elva",28,"henan")); 7 return list; 8 } 9 }
客户端
1 public static void main(String[] args) 2 throws InterruptedException, RemoteException { 3 // TODO Auto-generated method stub 4 threadMessage("start "); 5 //建立服务对象 6 PersonServiceStub personService=new PersonServiceStub(); 7 PersonServiceStub.GetList getList=new PersonServiceStub.GetList(); 8 //建立回调函数 9 PersonServiceCallbackHandler callback=new PersonServiceCallbackHandler(){ 10 public void receiveResultgetList(GetListResponse response){ 11 threadMessage("callback "); 12 PersonEntity[] list=response.get_return(); 13 for(Integer n=0;n) 14 displayPersonProperty(list[n]); 15 } 16 }; 17 //启动异步服务 18 personService.startgetList(getList,callback); 19 Thread.sleep(500); 20 } 21 22 //显示线程id 23 private static void threadMessage(String data){ 24 Thread thread=Thread.currentThread(); 25 System.out.println(data +"threadId: "+thread.getId()); 26 } 27 28 //显示对象信息 29 private static void displayPersonProperty(PersonEntity person){ 30 System.out.println("Id:"+person.getId() +" Name:" 31 +person.getName() +" Age:"+ person.getAge() 32 +" Address:"+person.getAddress() ); 33 }
测试结果
回到目录
本章小结
Aixs 的主要特点在于其操作的灵活性,它能对请求和回发的 SOAP 信息直接进行处理,在头文件或者自定义节点中加入数据。特别在Axis 2.x 引入 AXIOM 后,其优点更为突出。它使用 StAX(Streaming API for XML ) 拉式(Pull)解析器,可尽量减轻对系统资源的压力。Axis 2.x 使用 Module 模块化的部署方式,使系统功能分割更为简单, Module 可以独立于服务进行开发。为了解决客户端为等待返回数据而长时间处于柱塞状态,Axis 2.x 还加入异步操作的方法,提高了客户端的运行效率。
由于本人并非 JAVA 方面的专家,文章难免存在错误之处,敬请读者点评。
对JAVA开发有兴趣的朋友欢迎加入
QQ群:Java高级编程(174850571)
相关文章
深入剖析 RabbitMQ —— Spring 框架下实现 AMQP 高级消息队列协议
学习笔记:JAVA RMI远程方法调用简单实例
作者:风尘浪子
http://www.cnblogs.com/leslies2/archive/2012/09/07/leslies2Blog_java_axis.html
原创作品,转载时请注明作者及出处