通过 Axis2 运行时部署和使用简单 Web

2006 年 6 月 13 日

本文介绍 Axis2 的新体系结构,并说明如何通过 Axis2 部署和使用 Web 服务。本文是有关通过 Axis2 运行时开发 Web 服务的系列文章的第 1 部分(共两部分)。Axis2 是下一代 Apache Axis Simple Object Access Protocol (SOAP) 运行时。

引言

Axis2 是下一代 Apache Axis。Axis2 虽然由 Axis 1.x 处理程序模型提供支持,但它具有更强的灵活性并可扩展到新的体系结构。Axis2 基于新的体系结构进行了全新编写,而且没有采用 Axis 1.x 的常用代码。支持开发 Axis2 的动力是探寻模块化更强、灵活性更高和更有效的体系结构,这种体系结构可以很容易地插入到其他相关 Web 服务标准和协议(如 WS-Security、WS-ReliableMessaging 等)的实现中。

Axis2 的特性包括:

  • 采用名为 AXIOM(AXIs Object Model,Axis 对象模型)的新核心 XML 处理模型
  • 支持 In-Only 和 In-Out 消息交换模式 (MEP)
  • 阻塞和非阻塞客户端 API(应用程序编程接口)
  • 支持内置的 Web 服务寻址 (WS-Addressing)
  • 支持 XMLBeans 数据绑定
  • 新部署模型
  • 支持超文本传输协议 (HTTP)、简单邮件传输协议 (SMTP) 和传输控制协议 (TCP) 等传输协议

 

本系列文章以 Axis2 0.92 版本为基础。您可以在 Apache 网站获得 Axis2 的最新版本。


通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


Axis 体系结构概述


图 1. Axis2 体系结构
通过 Axis2 运行时部署和使用简单 Web

Axis2 体系结构将逻辑与状态分离;这允许在并行线程中执行逻辑。服务和调用的静态状态和动态状态分别存储在 DescriptionContext 类中。Axis2 体系结构是使用 7 个独立模块实现的。

  1. 信息模型:此模块管理 SOAP 引擎的状态。该模型定义一组用于存放状态的类,而引擎管理这些信息对象的生命周期。信息模型包含两种用于存放状态的类。Description 类存放本质上是静态的且存在于 Axis 引擎实例的整个生命周期中的数据(如传输、服务和操作的配置)。Context 类存放调用上下文中有效的服务和操作的动态信息,例如当前请求和响应 SOAP 消息、From 地址、To 地址和其他元素。
  2. XML 处理模型:Axis2 引入了一个名为 AXIOM 的新模型,用于处理 SOAP 消息。AXIOM 使用 StAX (Streaming API for XML) 来解析 XML。StAX 是一个标准的流式 Pull 解析器 Java™ API。AXIOM 非常精巧,不会减慢 XML 信息集的构建速度——换句话说,对象只有在绝对必要时才会创建。总体而言,AXIOM 和 Axis2 所占用的内存要小于 Axis 1 所占用的内存。
  3. SOAP 处理模型:Axis2 体系结构定义了两个管道(或流),分别称为 InPipe (InFlow) 和 OutPipe (OutFlow),用于处理服务器端的请求消息和响应消息。在客户端,这两个管道是反向的——换句话说,SOAP 请求消息流经 OutPipe,而响应消息流经 InPipe。管道或流包含一系列分为阶段的处理程序。阶段按照预先定义的顺序执行,如上面的图 1 所示。除预先定义的阶段和处理程序集外,用户还可以在操作级别、服务级别或全局级别配置用户阶段和相关处理程序。处理程序充当 SOAP 消息的拦截器,可以处理 SOAP 消息的 Header 或 Body。InPipe 是通过以下阶段进行配置的:
    • TransportIn
    • PreDispatch
    • Dispatch
    • PostDispatch
    • PolicyDetermination
    • User phases
    • Message validation
    我们将在本系列文章的第 2 部分中详细介绍上述各阶段。请求消息在通过 Inpipe 中配置的所有阶段后,到达 MessageReceiver,然后由 MessageReceiver 调用实际服务实现。服务器的 OutPipe 包含以下阶段:
    • Message initialization
    • Policy determination
    • User phases
    • MessageOut
    用户配置的阶段位于这两个管道的用户阶段(User phases) 部分。如果在执行这些管道的过程中发生错误,则这些错误将通过 InFaultPipe 或 OutFaultPipe 管道。收到 Fault 消息后,在客户端调用 InFaultPipe;如果某个调用导致将错误发送到客户端,则在服务器端调用 OutFaultPipe。用户可以将处理程序添加到预先定义的阶段,并且按照这些处理程序运行的顺序进行配置。
  4. 部署模块:此模块配置 Axis 引擎并部署服务和模块。axis2.xml(在 webapps/axis2/WEB-INF 中)包含 Axis2 引擎的全局配置,包括:
    • 全局模块 (Global modules)
    • 全局接收器 (Global receivers)
    • 传输 (Transports)
    • 用户阶段定义 (User phase definitions)
    每个服务的配置都包含在服务存档的 services.xml 文件中。本文稍后将详细讨论此文件。
  5. WSDL 和代码生成:此模块从 WSDL 文件中生成客户端存根和服务器框架代码。Axis2 代码生成器发出采用正确 XML 样式表的 XML 文件,以用所需语言生成代码。
  6. 客户端 API:Axis2 客户端 API 调用遵循 WSDL 2.0 定义的 In-Only 和 In-Out 消息模式的操作。客户端 API 支持 In-Out 操作的阻塞和非阻塞调用。
  7. 传输:此模块包含与传输层交互的处理程序。传输处理程序有两种类型:TransportListener 和 TransportSender。TransportListener 从传输层接收 SOAP 消息,然后将其传送到 InPipe 进行处理。TransportSender 发送通过指定传输从 OutPipe 接收到的 SOAP 消息。Axis2 提供 HTTP、SMTP 和 TCP 的处理程序。对于 HTTP 传输,服务器端上的 AxisServlet 和客户端上的一个简单的独立 HTTP 服务器(由 Axis2 提供)充当 TransportReceiver。

通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


部署 Axis2

部署 Axis2 与部署 Axis 1 一样简单。首先在 Axis2 二进制代码分发包的 webapps 目录下查找 Axis2 Web 应用程序 axis2.war。在 servlet 容器中部署此 war 文件。在 Tomcat 中,如果已在服务器配置中将 unpackWARs 设置为 True,则只需将 axis2.war 复制到 $TOMCAT_HOME/webapps 目录即可部署 Axis2。请立即启动 Tomcat 并访问 http://localhost:<port>/axis2。将显示 Axis2 欢迎页,单击此页上的 Validate 链接。您应到达 Axis2 Happiness page,不会出现任何错误。


通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


开发 StockQuoteService

下面介绍如何使用 In-Only subscribe() 和 In-Out getQuote() 这两个操作来开发 StockQuoteService。subscribe() 操作将预订指定代号的每小时报价,而 getQuote() 将获得指定代号的当前报价。

下面的清单 1 是 StockQuoteService 的实现示例:


清单 1. StockQuoteService 实现
				
package stock;
import org.apache.axis2.om.OMAbstractFactory;
import org.apache.axis2.om.OMElement;
import org.apache.axis2.om.OMFactory;
import org.apache.axis2.om.OMNamespace;
		
public class StockQuoteService {
		 
  public void subscribe(OMElement in){
    String symbol = in.getText();
    System.out.println("Subscription request for symbol ="+symbol);
    // put the actual subscribe code here...    
  }
		  
		  
  public OMElement getQuote(OMElement in){
		  
    // Get the symbol from request message
    String symbol = in.getText();
		    
    int quote = 0;
    if(symbol.equals("IBM")){
      quote = 100;
    }
    // Put more quotes here ...
		    
    // Create response
    OMFactory fac = OMAbstractFactory.getOMFactory();
    OMNamespace omNs = fac.createOMNamespace(
      "http://www.developerworks.com/example", "example");
    OMElement resp = fac.createOMElement("getQuoteResponse", omNs);
    resp.setText(String.valueOf(quote));
    return resp;      
  }
}

对方法签名感到很疑惑?我们将讨论上述方法中的 OMElement


通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


部署服务

部署描述符

在 Axis2 中,服务部署信息包含在 services.xml 文件(在 0.92 以前的版本中,此文件名为 service.xml)中。对于上述 StockQuoteService,服务部署描述符与下面的清单 2 类似。


清单 2. Services.xml
				
<service name="StockQuoteService">
  <parameter name="ServiceClass" locked="xsd:false">
    stock.StockQuoteService
  </parameter>
		
  <operation name="getQuote">
    <messageReceiver 
      class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
  </operation>
		
  <operation name="subscribe">
    <messageReceiver 
      class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver"/>
  </operation>
</service>

服务的 name 属性定义服务的名称。Axis2 使用服务的名称创建服务的端点地址,例如 http://localhost:<port>/axis2/services/<nameofservice>。因此,对于 StockQuoteService,服务端点为 http://localhost:<port>/axis2/services/StockQuoteServiceServiceClass 参数指定服务实现类。

每个 <operation> 元素定义服务中一个操作的配置。<operation> 的 name 属性应设置为服务实现类中方法的名称。messageReceiver 元素定义用于处理此操作的消息接收器。Axis2 针对 In-Only 和 In-Out 操作提供了两个无数据绑定的内置 MessageReceivers;org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver 用于 In-Only 操作,而 org.apache.axis2.receivers.RawXMLINOutMessageReceiver 用于 In-Out 操作。如果没有指定 messageReceiver,则 Axis2 将尝试使用 org.apache.axis2.receivers.RawXMLINOutMessageReceiver 作为缺省的 messageReceiver。上述 RAWXML 消息接收器将传入 SOAP 消息的 <Body> 的内容作为 OMElement(OMElement 是 XML 元素的 AXIOM 缩写)传递给服务实现。此操作应作为 OMElement 返回 SOAP 响应的 <Body> 元素包含的 XML 内容。这便解释了为何 subscribe() 和 getQuote() 操作采用和返回 OMElement。

services.xml 还可以包含分为 servicegroup 的多个服务。

打包

Axis 2 服务是作为 Axis Archive (.aar) 打包的。这是一个 JAR 文件(使用 jar 或 zip 实用程序创建),在存档的 META-INF 目录中打包了 services.xml 文件。StockQuoteService 在打包成 StockQuoteService.aar 时将具有以下结构:

./stock/StockQuoteService.class

./META-INF/services.xml

预先打包的 StockQuoteService 存档可以在本文的下载部分中找到。

部署

在 Axis2 中部署服务相当简单,只需将 .aar 文件复制到 servlet 容器的 axis2 Web 应用程序中的 axis2/WEB-INF/services 目录下即可。对于 Tomcat,此位置为 $TOMCAT_HOME/webapps/axis2/WEB-INF/services

另一种部署服务的好方法是使用 Axis2 管理控制台中的 Upload Service 工具。请转到 http://localhost:<port>/axis2,然后选择 Administration 链接。输入用户名和密码 admin/axis2,然后登录。(您可以在 axis2.xml 中配置用户名/密码。)在工具部分选择 Upload Service 链接,再选择 .aar 文件,然后单击 Upload。就是这样简单!如果上传成功,系统将显示一条绿色成功消息。服务即被部署,而且可随时调用。如果要在远程 Axis2 服务器上部署服务,则此功能非常方便。


通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


通过 Axis2 使用 Web 服务

Web 服务调用的特性由 MEP、传输协议以及客户端 API 的同步和/或异步行为决定。Axis2 当前支持 WSDL 2.0 定义的 In-Only 和 In-Out MEP。Axis2 客户端 API 支持服务的同步和异步调用。在调用 In-Out 操作时,在 API 级别和传输级别提供异步行为。API 级别异步是通过回滚获得的,它使用一个传输连接来同时传输请求和响应(例如,通过一个 HTTP 连接传输请求和响应)。在传输级别异步中,使用不同的传输连接分别发送请求和接收响应,例如使用 SMTP 进行传输时即如此。

下面是使用 Axis2 客户端 API 调用 In-Only 和 In-Out 操作的详细信息。

调用 In-Only 操作

org.apache.axis2.clientapi.MessageSender 类用于调用 In-Only 操作(如下面的清单 3 所示),而 In-Only 操作调用 StockQuoteServicesubscribe() 操作。


清单 3. 调用 In-Only 操作
				
try{
  EndpointReference targetEPR = new EndpointReference(
      "http://localhost:8080/axis2/services/StockQuoteService");
          
  // Make the request message
  OMFactory fac = OMAbstractFactory.getOMFactory();
  OMNamespace omNs = fac.createOMNamespace(
      "http://www.developerworks.com/example", "example");
  OMElement payload = fac.createOMElement("subscribe", omNs);
  payload.setText("IBM"); 
          
  // Send the request
  MessageSender msgSender = new MessageSender();
  msgSender.setTo(targetEPR);
  msgSender.setSenderTransport(Constants.TRANSPORT_HTTP);
  msgSender.send("subscribe", payload);         
  }catch (AxisFault axisFault) {
      axisFault.printStackTrace();
  }

MessageSender.send() 发送请求消息并将其立即返回。要使用的传输由 MessageSender.setSenderTransport() 指定。此示例通过 HTTP 发送消息。

调用 In-Out 操作

使用 org.apache.axis2.clientapi.Call 类可以方便地调用 In-Out 操作。调用 In-Out 操作时,此 Call 类支持下列 4 种模式:

  1. 阻塞单传输模式:这是调用 In-Out Web 服务操作最简单的方式。在操作完成和接收到响应或错误之前,服务调用被阻塞。它使用一个传输连接同时发送和接收响应,如下面的清单 4 所示。



    清单 4. 阻塞单传输模式
    						
    try {
                     
      EndpointReference targetEPR = new EndpointReference(
          "http://localhost:8080/axis2/services/StockQuoteService");
                   
      // Create request message
      OMFactory fac = OMAbstractFactory.getOMFactory();
      OMNamespace omNs = fac.createOMNamespace(
          "http://www.developerworks.com/example", "example");
        OMElement payload = fac.createOMElement("getQuote",omNs);
      payload.setText("IBM");
    
      // Create the call
      Call call = new Call();
      call.setTo(targetEPR);
    			
      call.setTransportInfo(Constants.TRANSPORT_HTTP,
        Constants.TRANSPORT_HTTP, false);
      // Invoke blocking
      OMElement result = call.invokeBlocking("getQuote", payload);
                   
      System.out.println("Quote ="+result.getText());
    }catch (AxisFault axisFault) {
        axisFault.printStackTrace();
    }
    

    代码的第一部分使用 AXIOM 创建请求消息。Call.setTransportInfo() 设置用于发送请求和获得响应的传输。Call.setTransportInfo() 操作的 Boolean 参数指出是否要使用不同的传输连接来分别发送请求和接收响应。在本例中,要求用一个 HTTP 连接发送请求和接收响应。

  2. 非阻塞单传输模式:在此调用模式中,只使用下面的一个传输连接获得非阻塞调用。如果在一个客户端应用程序中要完成多个 Web 服务调用,而且不希望每次调用都阻塞客户端,则需要此类行为。此时,如果响应可用,则调用立即返回且客户端得以回滚,如下面的清单 5 所示。



    清单 5. 非阻塞单传输模式
    						
    try {
      EndpointReference targetEPR = new EndpointReference(
          "http://localhost:8080/axis2/services/StockQuoteService");
                
      //Create the request
      OMFactory fac = OMAbstractFactory.getOMFactory();
      OMNamespace omNs = fac.createOMNamespace(
          "http://www.developerworks.com/example", "example");
      OMElement payload = fac.createOMElement("getQuote", omNs);
      payload.setText("IBM");
          
                
        // Create the call
        Call call = new Call();
        call.setTo(targetEPR);
                
        // Set the transport info.
        call.setTransportInfo(org.apache.axis2.Constants.TRANSPORT_HTTP,
          org.apache.axis2.Constants.TRANSPORT_HTTP, false);
                
      // Callback to handle the response
                
        Callback callback = new Callback() {
          
          public void onComplete(AsyncResult result) {
            System.out.println("Quote = "
              + result.getResponseEnvelope().getBody().getFirstElement()
                .getText());
          }
          
          public void reportError(Exception e) {
            e.printStackTrace();
          }
        };
                
                
      // Invoke non blocking
                
        call.invokeNonBlocking("getQuote", payload, callback);            
                
      //Wait till the callback receives the response.
                
        while (!callback.isComplete()) {
          Thread.sleep(1000);
        }
                
                
      call.close();
    } catch (AxisFault axisFault) {
      axisFault.printStackTrace();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    

    Call.invokeNonBlocking() 方法立即返回而不阻塞。Call.invokeNonBlocking() 采用 org.apache.axis2.clientapi.CallBack 的对象,如果响应来自服务,则将触发此对象。CallBack 有两个抽象方法 onComplete(AsynchResult)reportError(Exception),需要由具体的 CallBack 类实现这些方法。在服务调用正常完成后,Axis2 引擎调用 onComplete() 方法。在从服务器获得错误消息后,调用 CallbackreportError() 方法。Callback.isComplete() 将指出操作调用是否完成。

    因为上面两个方法使用一个传输连接来发送和接收消息,所以这些方法不适合长时间运行的事务。原因是在响应可用之前,传输连接可能会超时。要解决此问题,可使用两个不同的连接来分别发送请求和接收响应。但因为使用了其他传输连接来获得响应,因此需要将请求和响应关联起来。Axis2 支持 WS-Addressing,后者通过使用 <wsa:MessageID> 和 <wsa:RelatesTo> Header 可解决此问题。因此,如果使用两个传输,则支持对模块寻址,如下面两个模式所示。

  3. 阻塞双传输模式:此模式在以下情况下非常有用:服务操作在本质上是 In-Out,但使用的传输是单向的(如 SMTP)或服务执行需要很长时间且 HTTP 连接超时。请参见下面的清单 6



    清单 6. 阻塞双传输模式
    						
    try{
      EndpointReference targetEPR = new EndpointReference(
          "http://localhost:8080/axis2/services/StockQuoteService");
                    
      OMFactory fac = OMAbstractFactory.getOMFactory();
      OMNamespace omNs = fac.createOMNamespace(
          "http://www.developerworks.com/example", "example");
      OMElement payload = fac.createOMElement("getQuote",omNs);
      payload.setText("IBM");
            
      Call call = new Call();
        call.setTo(targetEPR);
    
        call.setTransportInfo(
          Constants.TRANSPORT_HTTP, Constants.TRANSPORT_HTTP, true);
    
        //Blocking Invocation
        OMElement result = call.invokeBlocking("getQuote", payload);
      System.out.println("Quote = "+result.getText());
    	
    }catch (AxisFault axisFault) {
        axisFault.printStackTrace();
    }catch (Exception ex) {
        ex.printStackTrace();
    }
    

  4. 非阻塞双传输模式:就 API 级别和传输级别的非阻塞而言,此模式提供了最大的灵活性,如下面的清单 7 所示。



    清单 7. 非阻塞双传输模式
    						
    try {
           
      EndpointReference targetEPR = new EndpointReference(
          "http://localhost:8080/axis2/services/StockQuoteService");
      OMFactory fac = OMAbstractFactory.getOMFactory();
      OMNamespace omNs = fac.createOMNamespace(
          "http://www.developerworks.com/example", "example");
      OMElement payload = fac.createOMElement("getQuote",omNs);
      payload.setText("IBM");
            
               
        Call call = new Call();
        call.setTo(targetEPR);
    
        call.setTransportInfo(
          Constants.TRANSPORT_HTTP, Constants.TRANSPORT_HTTP, true);
      // Callback to handle the response
               
        Callback callback = new Callback() {
        public void onComplete(AsyncResult result) {
          System.out.println("Quote = "
              + result.getResponseEnvelope().getBody().getFirstElement()
            .getText());
          }
    
        public void reportError(Exception e) {
            e.printStackTrace();
          }
        };
                
    
      // Non-Blocking Invocation
              
        call.invokeNonBlocking("getQuote", payload, callback);
      // Wait till the callback receives the response.
              
        while (!callback.isComplete()) {
          Thread.sleep(1000);
        }
        call.close();          
    }catch (AxisFault axisFault) {
          axisFault.printStackTrace();
    }catch (Exception ex) {
          ex.printStackTrace();
    }
    


通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


总结

在这一由两部分组成的系列的第 1 部分中,您了解了 Axis2 体系结构以及如何使用不同的调用模式来部署和使用一个简单的 Web 服务。在本系列的第 2 部分中,您将详细了解该体系结构,以及如何通过添加处理程序和 Axis2 消息接收器来自定义 Axis2。您还将了解如何使用 Axis2 提供的 XMLBeans 数据绑定支持。



通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web
回页首


下载

描述 名字 大小 下载方法
StockQuoteService Axis2 Archive file StockQuoteService.aar 3 KB  FTP
通过 Axis2 运行时部署和使用简单 Web
通过 Axis2 运行时部署和使用简单 Web 关于下载方法的信息 通过 Axis2 运行时部署和使用简单 Web 通过 Axis2 运行时部署和使用简单 Web Get Adobe® Reader®


参考资料

学习

获得产品和技术
  • 获得来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。您可以免费下载产品的评估版,也可以选择 Linux® 或 Windows® 版本的 developerWorks 免费软件评估工具包


讨论


作者简介

通过 Axis2 运行时部署和使用简单 Web

 

通过 Axis2 运行时部署和使用简单 Web

Gopalakrishnan U 目前是 IBM India Software Labs 中 Web Services Competency 团队的一员。他最近两年一直在 IBM 工作,有超过三年半的 Java 和 Web 技术经验。在过去的一年里,他致力于 Web 服务技术的研究,为 IBM Web Services ToolKit (WSTK) 做出了贡献。您可以通过 mailto:[email protected]?cc= 与 Gopalakrishnan 联系。


通过 Axis2 运行时部署和使用简单 Web

 

通过 Axis2 运行时部署和使用简单 Web

Shreevidya Rao 是 IBM Software Labs 中 Research Group 的软件工程师。她过去一年在 IBM 工作,有五年的 Java 和 J2EE 经验。她目前致力于开发分布式可管理的记录程序。您可以通过 mailto:[email protected]?cc= 与 Shreevidya Rao 联系。