利用Axis实现基于SOAP的Web Service

来源:互联网 发布:如何优化发展环境 编辑:程序博客网 时间:2024/05/22 03:43
利用Axis实现基于SOAP的Web Service

 

简介
这是一篇用Java不到2周的新手写的关于利用Axis实现基于SOAP的Web Service的纪实性文章,里面不敢介绍什么SOAP,Axis,Web Service之类的理论知识,因为我至今还不是很懂这些,其中涉及到的你可能感兴趣的问题是:
   1.如何用JBuilder 9写Web Service的服务器端和客户端;
   2.如何发布Web Service;
   3.如何解决Web Service中自定义对象的传递问题.
我使用的各个主要开发抱/工具/平台的情况如下:
       1. JDK 1.4.0.01
       2. JBuilder 9.0
       3. Axis 1.1(past name is Apache SOAP 3.0)
       4. Xerces 2.5.0
       5. Javamail 1.3.1
       6. Jaf 1.0.2
       7. Tomcat 4.1

关键字 Axis,Web Service,SOAP,JBuilder

*为什么是Axis而不是Apache SOAP? 

 我最近跳槽了,在以前的公司莫名的郁闷了很久,终于是迈出了这一步,新公司用Java,天,Java,对我来说用Java是近四年前的事情了,那时候还是JDK1.1.7A,我有些莫名的紧张,刚来接到的活是:”做一个Web Service提供给别人调用”….Web Service,我在Windows平台上倒是用过,无非是找一个Java世界中的SOAP实现(也就是一个 SOAP Toolkit),然后调用就完了…首先我找到的是Apache SOAP 2.2,我满心欢喜的准备开始了…还是等等吧,我劝自己冷静一点,看看文档再说…于是我泡上一杯茶到http://ws.apache.org/axis/index.html一顿乱看,这一看把我吓了一跳…有文字为证:

     Apache SOAP                   Axis
----------------------------------------------------------------------
     really old                    third generation
     really slow                   much faster, but not as fast as many
     no WSDL support               WSDL support
     proprietary API               JAX-RPC API
     RPC/encoded only              RPC/encoded and Doc/literal
     interoperability issues       very interoperable
     extensiblity issues           very extensible
     low level API for headers     easy handler support for headers

上司给我的文档中提到了WSDL这玩意,而上面的表格说明Apache SOAP就不支持WSDL…很自然我的,我选择了Axis,我现在对Axis的认识就是:它是Apache SOAP的后续版本,本来是叫Apache SOAP 3.0的,但是人家为了噱一点就去了这个名字.
     Axis和Apache SOAP都是SOAP协议的实现,但是基于上面提到的理由我选择了Axis.

*安装和配置Axis

这个请参看http://ws.apache.org/axis/index.html上面写的很详细,其实Axis的安装和配置很简单,从apache网站上Down下1.1的安装文件,解压缩,然后一份拷贝到服务器上,一份拷贝到客户端,然后就是配置了,最主要的就是环境变量的设置了:

    A. Client(Windows 2000/NT/XP)

         set AXIS_HOME = c:/axis
         set AXIS_LIB  = %AXIS_HOME%/lib
         set AXISCLASSPATH = %AXIS_LIB%/axis.jar;
                             %AXIS_LIB%/commons-discovery.jar;
                             %AXIS_LIB%/commons-logging.jar;
                             %AXIS_LIB%/jaxrpc.jar;%AXIS_LIB%/saaj.jar;
                             %AXIS_LIB%/log4j-1.2.8.jar;
                             %AXIS_LIB%/xml-apis.jar;
                             %AXIS_LIB%/xercesImpl.jar

     B. Server(Unix+Tomcat4.1.X)

                    set AXIS_HOME=/usr/axis
         set AXIS_LIB=$AXIS_HOME/lib
         set AXISCLASSPATH=$AXIS_LIB/axis.jar:$AXIS_LIB/commons-discovery.jar:
                           $AXIS_LIB/commons-logging.jar:
                           $AXIS_LIB/jaxrpc.jar:$AXIS_LIB/saaj.jar:
                           $AXIS_LIB/log4j-1.2.8.jar:$AXIS_LIB/xml-apis.jar:
                           $AXIS_LIB/xercesImpl.jar
         export AXIS_HOME;
         export AXIS_LIB; 
         export AXISCLASSPATH

环境变量很重要也是很容易搞错的地方,你可以设置好以后在控制台下输入set命令(我假设你此时在Win2000/XP下)检查一下自己的环境变量是否设置正确了…像什么找不到指定类之类的错误很可能就是因为环境变量没有设置正确,遇到这类问题别急着哭着喊着去csdn贴贴子,检查自己的环境变量设置先.

*我要写的Web Service
 
真的当然不能说,职业道德嘛,所以假设叫CommitOrderReq:
  
     public  ResultInfo CommitOrderReq (OrderFormInfo ofi)

这是一个公开给别人提交订单的Web Service,客户端要传给我一个OrderFormInfo对象,这个对象描述了订单的信息,我返回给客户端一个ResultInfo对象.下面是OrderFormInfo和ResultInfo的定义:

class OrderFormInfo
{
    public OrderFormInfo( ) { };   // $$$ - 1
    
    String m_strOrderName;
    int      m_nOrderID;

    OrderOwner m_objOrderOwner;   // $$$ - 2

    String get_OrderName( );
    void   set_OrderName(String strIn);

    String get_OrderID( );
    void   set_OrderID(int nIn );
}

class ResultInfo
{
    public ResultInfo( ) {  };      // $$$ - 3

    String  m_strResult;
   
    String get_Result ( );
    void   set_Result (String strIn);

}

有两个地方需要注意的,这两个类最好遵从JavaBean的规范(“遵从JavaBean的规范”,我甚至都不知道这么说对不对,呵呵,对Java世界还不熟悉…见谅呀各位),不遵从的话你到时候会在客户端传递对象给服务器的时候遇到麻烦的,怎么遵从呢?哎..就是:
  1.一定要有默认构造函数(就是不带参数的构造函数),见$$$ -1,$$$ -3;
  2.像Microsoft COM中的某种属性一样针对各个成员变量提供get,set方法.

请注意,在OrderFormInfo对象里面又聚合了一个OrderOwner对象,这种样子参数在实际工作中经常会遇到的情况,一开始我没有在意这一点,没觉得有什么不对…后来大家会知道发生了什么…

*用JBuilder 9写一个Web Service
   
 File|New,新建一个工程,然后把自己让Web Service做的事情写完了…其中艰辛就不说了,都不知道该怎么去查Java那浩瀚的类…Sun要是有MSDN之类的帮助就好了…
  

*部署Web Service
   
        A.Select File|New Project to display the Project wizard.Select “Web Service” tab and then click “Web Service Configuation”.
        B. Choose Apache Axis from the Toolkit drop-down list,enter ”webApp” in another field;
        C. Select File|New Project to display the Project wizard.Select “Web Service” tab,click “Export as a web service”.
        D.In “Export as a web service – Step 1 of 7”wizard click browser-button that belongs to “Interface or Class”.
        E.In “Choose Class or Intersface to Export As Web Service” dialog,find and select the “firstjavawebservice.HelloAxis” item.It is the class that you want to export as web service.
        F.Run this project,it will display “JBuilder Apache Axis Admin Console”page,click “view”to check whether your web service is successfully deploy on your local tomcat server.You should conform WSDL file also.
        G.Click finish to close the Project wizard.
    
   别以为这就完了,假设我刚才我在Step B中填写的是perdubug_ws,接下来我就把perdubug_ws/WEB-INF目录下的东西拷贝到服务器上的/Tomcat 4.1/webapps/axis/WEB-INF目录下,这就算部署了…我就很纳闷是否JBuilder 9提供了这样的功能是的我可以直接在本地编译完我的代码然后直接部署到远端的服务器上呢?反正我是没找到…
   部署完了再在浏览器里面输入http://服务器地址:端口号/axis/,就会出现你的Axis的页面否这说明你没有配置好Axis…点击View看一下你的服务是否部署成功了,如果成功的话你可以看到你的服务名旁边有一个(WSDL),记下它的URL后面用得到,我们在这称呼这个URL为wsdl_URL,主要是为了好记.

*用JBuilder 9写调用Web Service的客户端
 
        File|New|Web Service,选Import a web service,没错,wsdl_URL就是我们要import的WSDL,倒入成功后你会看到JBuilder会根据这个WSDL产生几个文件,这些文件封装了axis和SOAP打交道的细节,你不用再关心什么call之类的对象了,你几乎可以把web service当作本地的方法来使用…
        然后你再建立一个你的客户端java文件,如下(其中有看不明白的可以参看axis的例子,比我的详细多了):

   public class ws_invoker
   {
       public static void main(String[] args)
       {
          Perdubug_wsServiceLocator pwsl = new Perdubug_wsServiceLocator();
          OrderFormInfo                   ofi    = new OrderFormInfo ();
          ResultInfo                          ri     = null;

          Perdubug_ws pw;

          OrderOwner  oo = new OrderOwner();
          oo.set_XXX( ); 

          ofi .set_XXX( );

          try {

             pw   = pwsl.getws_entry();
             ri     = pw.syncOrderRelationReq(ofi);

           } catch (Exception e) {

               System.out.println("exception: " + e.getMessage());
           }

         System.out.println("Result: " + ri.get_XXX());

       }
}

*运行客户端,发生了什么

靠,一运行JBuilder告诉我说什么OrderOwner没有反序列化之类的提示…序列化,反序列化…我猛差了一阵子资料,觉得不对,这些难道axis没帮我做了吗?还需要自己来做吗??这个问题困扰了几根烟的时间,是不是所谓的”简单对象存取协议”就简单在这呀?我上google股沟了一下我的问题,发现很多人在讨论说要改这个配哪个的…郁闷….
我把OrderOwner相关代码注释掉,一运行,就可以…也就是说JBuilder或者是Axis没有把我的OrderOwner正确的序列化导致服务器那边就不认识我的宝贝OrderOwner对象,解决问题的关键在哪呢?我查了一下文档,说在Tomcat 4.1/webapps/axis/WEB-INF/server-config.wsdd,我们先看一下导致我很烦恼的那个server-config.wsdd的片断:
  
  <service name="Perdubug_ws" provider="java:RPC">
  <parameter name="allowedMethods" value="*"/>
  <parameter name="scope" value="Request"/>
  <parameter name="className" value="mpeonasia_ws.ws_entry"/>
  <typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" 
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        qname="ns1: OrderformInfo " 
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" 
        type="java: samsungasia_ws.OrderformInfo" 
        xmlns:ns1="http:// samsungasia _ws"/>
  <typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" 
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        qname="ns2: ResultInfo " 
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" 
        type="java:samsungasia_ws. ResultInfo" 
        xmlns:ns2="http:// samsungasia _ws"/>
 </service>

猜都猜得出来typeMapping是用来告诉服务器如何Mapping那些JavaBean类的,我一葫芦画瓢手动的添加了OrderOwner项:
<service name="Perdubug_ws" provider="java:RPC">
  <parameter name="allowedMethods" value="*"/>
  <parameter name="scope" value="Request"/>
  <parameter name="className" value="mpeonasia_ws.ws_entry"/>
  <typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" 
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        qname="ns1: OrderformInfo " 
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" 
        type="java: samsungasia_ws.OrderformInfo" 
        xmlns:ns1="http:// samsungasia _ws"/>
  <typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" 
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        qname="ns2: ResultInfo " 
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" 
        type="java:samsungasia_ws. ResultInfo" 
        xmlns:ns2="http:// samsungasia _ws"/>
  <typeMapping deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory" 
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
        qname="ns3: OrderOwner" 
        serializer="org.apache.axis.encoding.ser.BeanSerializerFactory" 
        type="java:samsungasia_ws. OrderOwner" 
        xmlns:ns2="http:// samsungasia _ws"/>
 </service>

然后在运行我的客户端,就好了…不要以为我会说什么我从中得到了什么经验之类的话,我只想说:为什么JBuilder或者是Axis不把这种事情帮我做了而要我自己来动手?最起码在编译服务器端的web service的时候提示一下说:GG,这个类没有被写进server-config.wsdd也…或者在JBuilder里面增加对server-config.wsdd的配置界面吧.
心情郁闷,为了这样的问题,我花了很多时间看如下电子书:

O'Reilly - Java and SOAP.pdf
O'Reilly - Java Web Service.pdf
O'Reilly - Programming Web Services with SOAP.pdf
O'Reilly - Programming Web Services With XML RPC.pdf
O'Reilly - Web Servies Essentials.pdf 

差一点就因为这个问题把SOAP的什么Envelop,Body,Head,PortType…文法和WSDL的文法搞了一遍了,我觉得没什么意思,浪费我的时间,我现在事情多的打堆.
用Java我的感觉就是:可选的方案多的打堆,等你搞明白了这个那个又热起来了,一堆的人又会对你说,快换吧,那个更爽…..爽什么呀,还不如我在CSDN乱写些东西爽别人呢..J
 

原创粉丝点击