Java远程通信技术——Axis实战

前言

在 Internet 网络覆盖全球的今天,网络通信已经是当今软件开发过程中离不开的话题。在常用的Windows、Liunx、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-Axis</display-name>
 3     <listener>
 4       <listener-class>org.apache.axis.transport.http.AxisHTTPSessionListener</listener-class>
 5     </listener>
 6     <servlet>
 7       <display-name>Apache-Axis Servlet</display-name>
 8       <servlet-name>AxisServlet</servlet-name>
 9       <servlet-class>
10           org.apache.axis.transport.http.AxisServlet
11       </servlet-class>
12     </servlet>
13     <!-- 服务管理 -->
14     <servlet>
15       <display-name>Axis Admin Servlet</display-name>
16       <servlet-name>AdminServlet</servlet-name>
17       <servlet-class>
18           org.apache.axis.transport.http.AdminServlet
19       </servlet-class>
20       <load-on-startup>100</load-on-startup>
21     </servlet>
22     <!-- 对输入输出的SOAP信息进行检测 -->
23     <servlet>
24       <display-name>SOAPMonitorService</display-name>
25       <servlet-name>SOAPMonitorService</servlet-name>
26       <servlet-class>
27           org.apache.axis.monitor.SOAPMonitorService
28       </servlet-class>
29       <init-param>
30         <param-name>SOAPMonitorPort</param-name>
31         <param-value>5001</param-value>
32       </init-param>
33       <load-on-startup>100</load-on-startup>
34     </servlet>
35     <servlet-mapping>
36       <servlet-name>AxisServlet</servlet-name>
37       <url-pattern>/servlet/AxisServlet</url-pattern>
38     </servlet-mapping>
39     <servlet-mapping>
40       <servlet-name>AxisServlet</servlet-name>
41       <url-pattern>*.jws</url-pattern>
42     </servlet-mapping>
43     <servlet-mapping>
44       <servlet-name>AxisServlet</servlet-name>
45       <url-pattern>/services/*</url-pattern>
46     </servlet-mapping>
47     <servlet-mapping>
48       <servlet-name>SOAPMonitorService</servlet-name>
49       <url-pattern>/SOAPMonitor</url-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     <!--在浏览器暴露服务 PersonService-->
 4     <service name="PersonService" provider="java:RPC">
 5          <!--绑定服务实现类型 PersonServiceImpl-->
 6         <parameter name="className"  value="axis.serviceImpls.PersonServiceImpl" />
 7         <parameter name="allowedMethods" value="*" />
 8         <parameter name="scope"  value="application" />
 9     </service>
10     <!--当接收到 http 请求时交由 URLMapper 进行处理-->
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     List<PersonEntity> getListByName(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<PersonEntity> getListByName(String name){
44         List<PersonEntity> list=new LinkedList<PersonEntity>();
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    <!--ServiceClass指定实现服务的类。   -->  
 4    <parameter name="ServiceClass" locked="false">
 5        axis2.serviceImpl.PersonServiceImpl
 6    </parameter>  
 7    <!-- operation 与Java Class中方法名对应。 -->  
 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          List<PersonEntity> personList=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         List<BookEntity> computerTypeList=getBooks(
 48                 publishing,"computer");
 49         OMElement computerElement=convertToOMElement(
 50                 computerTypeList,"computer",omNamespace);
 51         
 52         //获取文学类书本子节literatureElement
 53         List<BookEntity> 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<BookEntity> 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<BookEntity> getBooks(String publishing,String type){
 87         List<BookEntity> list=new ArrayList<BookEntity>();
 88         for(BookEntity book:virtualDatabase(publishing))
 89             if(book.getType()=="computer")
 90                 list.add(book);
 91         return list;
 92     }
 93     
 94     //虚拟数据
 95     private List<BookEntity> virtualDatabase(String publishing){
 96         List<BookEntity> list=new ArrayList<BookEntity>();
 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    <!--ServiceClass指定实现服务的类。   -->
4    <parameter name="ServiceClass" locked="false">axis2.serviceImpl.BookServiceImpl</parameter>  
5    <!-- operation 与Java Class中方法名对应。 -->  
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         List<BookEntity> list=convertToBooks(computerBookElement);
24         for(BookEntity book:list)
25             DisplayBook(book);
26     }
27 
28     //把XML数据转换为BookEntity对象集
29     private static List<BookEntity> convertToBooks(OMElement element)
30             throws AxisFault{
31         List<BookEntity> list=new ArrayList<BookEntity>();
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 三种消息交换模式。而