這些天稍微玩了一下Axis,以前做WebServices都使用JBuilder,感覺做WebServices如此簡單,現在自己手動做,原來也是如此簡單。高興之余寫一個簡單的初學手冊,就算是學習成果吧。當然對Axis理解的還不很深,所以錯誤之處望指點。

Axis是一個實現WebServiceFrameworkApache Web Services Projecthttp://ws.apache.org )的一個之項目,現在這個項目有很多之項目 Axis(http://ws.apache.org/axis/ )是其中一員,還有XML-RPC(這個也是我比較喜歡的東東J)。

現在Axis主要由兩個版本一個是Axis一個是Axis2。兩個好象有比較多的不同,我這里說的是Axis,過幾天演技一下Axis2,然后再寫一篇吧。

好了現在開始做個WebService 吧:

第一步當然是先去Axis主頁下載一個來啦。下Release就行,最新的是1.2.1,source好象沒有打包的只有CVS的。下來以后解壓縮,主要有以下文件夾

Docs 顧名思義,這里放的是文檔,其實Axis的文檔作的很好,我就是按照它的User Guide一步步做下來的。

Lib 運行Axis時要用到的jar包,要完全正常運行還缺兩個mail.jar activation.jar 這兩個是javaMail包,到處都能弄到。

Samples Axis自帶的例子包括很多種應用

Webapps  Axis是發布到Servlet Container中的,要把Axis集成到你的項目中,就把這個文件夾里的內容合并到你的項目中就行了。

還有一個xmls文件夾,放得是一些可能用到的xml例子。

 

第二步,建一個項目,Web項目,用Eclipse或者Idea都可以啊。如果你非要用記事本類的東西,我也不攔著你。

Axis中的Webapps\axis文件夾下的東西統統Copy到你的Web文件夾下。其實有些東西是沒用的,比如classes文件夾里的東西都可以去掉了,還有那幾個jws文件也沒有用。雖然axis最方便的發布WebServices的方法是把你的.java改成.jws的放到Web發布文件夾下的根目錄下,但是這種方法沒有什么適用價值。然后運行以下Tomcat(或者其他的Application Server)。然后瀏覽一下你的剛剛發布的這個項目,如果正常的話就可以看到Axis的默認畫面,
axis1.jpg
這個頁面不是必須的,在真正項目開發中可以把它去掉或換個名字。點擊
List連接進入已經發布的WebServices列表。
axis2.jpg
開始時應該只有
AdminServiceVersion。后面兩個就是我們在下面要做的WebServices。

 

第三步,如果上面的一切正常,就可以正式開始做WebServices了。首先做一個Services實現類。Calc.java有兩個方法plussubtract。這個Service所用到的數據類型都是基本類型。

public class Calc {
    
public int plus(int a,int b){
        
return a+b;
    }

    
public int substract(int a,int b){
        
return a-b;
    }
}

然后在WEB-INF目錄下加入一個server-config.wsdd。這是WebServices的發布描述文件,作用類似于web.xml。它有自己的格式,但是具體的標記是什么樣子的,在Axis的文檔中沒有詳細的一一列出,只是提到了常用的一些。在axis的源碼中有一些wsddXSD文件,如果你用的是IDEA可以把這些XSD映射到uri,這樣編輯器就有提示了。

下面這我們本文中的server-config.wsdd的樣子:

<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig"
            xmlns:java
="http://xml.apache.org/axis/wsdd/providers/java"
            xmlns:handler
="http://xml.apache.org/axis/wsdd/providers/handler" xmlns="http://xml.apache.org/axis/wsdd/">
    
<globalConfiguration name="defaultClientConfig">
        
<requestFlow name="RequestFlow1">
            
<handler name="Handler1" type="java:org.apache.axis.handlers.JWSHandler">
                
<parameter name="scope" value="session"/>
            
</handler>
            
<handler name="Handler2" type="java:org.apache.axis.handlers.JWSHandler">
                
<parameter name="scope" value="request"/>
                
<parameter name="extension" value=".jwr"/>
            
</handler>
        
</requestFlow>
    
</globalConfiguration>
    
<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
    
<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>
    
<handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
    
<transport name="http">
        
<requestFlow name="RequestFlow1">
            
<handler name="Handler1" type="URLMapper"/>
            
<handler name="Handler2" type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
        
</requestFlow>
    
</transport>
    
<transport name="local">
        
<responseFlow name="ResponseFlow1">
            
<handler name="Handler1" type="LocalResponder"/>
        
</responseFlow>
    
</transport>
    
<service name="AdminService" provider="java:MSG">
        
<parameter name="allowedMethods" value="AdminService"/>
        
<parameter name="enableRemoteAdmin" value="false"/>
        
<parameter name="className" value="org.apache.axis.utils.Admin"/>
        
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
    
</service>
    
<service name="Version" provider="java:RPC">
        
<parameter name="allowedMethods" value="getVersion"/>
        
<parameter name="className" value="org.apache.axis.Version"/>
    
</service>
    
<service name="CalcService" provider="java:RPC">
        
<parameter name="allowedMethods" value="*"/>
        
<parameter name="className" value="org.mstar.ws.Calc"/>
        
<parameter name="scope" value="request"/>
    
</service>
    
<service name="FooService" provider="java:RPC">
        
<parameter name="allowedMethods" value="*"/>
        
<parameter name="className" value="org.mstar.ws.FooService"/>
        
<parameter name="scope" value="session"/>
        
<typeMapping encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                     xmlns:ns1
="http://ws.mstar.org"
                     qname
="ns1:FooBean"
                     languageSpecificType
="java:org.mstar.ws.FooBean"
                     serializer
="org.apache.axis.encoding.ser.BeanSerializerFactory"
                     deserializer
="org.apache.axis.encoding.ser.BeanDeserializerFactory"
                     name
="FooBean"/>
        
<requestFlow name="requestFlow1">
            
<handler name="Handler1" type="java:org.mstar.ws.FooHandler"/>
        
</requestFlow>
        
<responseFlow>
            
<handler name="Handler1" type="java:org.mstar.ws.FooHandler"/>
        
</responseFlow>
    
</service>
</deployment>

這個文件比Axis自帶的那些deploy.wsdd要多很多東西,在Axis的文檔中它使用命令來把對deploy.wsdd進行發布的。在我的例子中是直接把server-config.wsdd寫好放到WEB-INF下。所以要把Service上面那些東西加上,否則系統不能正常運行。

    <service name="CalcService" provider="java:RPC">
        
<parameter name="allowedMethods" value="*"/>
        
<parameter name="className" value="org.mstar.ws.Calc"/>
        
<parameter name="scope" value="request"/>
    
</service>

Calc的發布描述。其中scope屬性默認是request所以不寫也可以。其他parameter看名字就知道干什么的了。這樣你在List頁面中就可以查看CalcServiceWSDL了。

第四步就是寫客戶端程序了。

WSClient.java

try {
            String endpoint 
= "http://localhost:8080/ws/services/CalcService";

            Service service 
= new Service();
            Call call 
= service.createCall();

            call.setTargetEndpointAddress(endpoint);
            call.setOperationName(
new QName("http://ws.mstar.org""plus"));
            Object[] params 
= new Object[2];
            params[
0= 10;
            params[
1= 20;
            Integer result 
= (Integer) call.invoke(params);
            System.out.println(
"Result:  " + result);
        } 
catch (Exception e) {
            e.printStackTrace();
        }

這是動態的調用WebService的方法,并不需要根據WSDL生成客戶端存根。但是對于Service中有復合類型的時候就不可以了。下一個例子講的就是如何做客戶端存根。如果這個例子能夠正常運行就可以了。

第五步做一個稍微復雜一點的例子,對于WebServices不能僅僅對簡單類型的數據進行操作,也應該能對復雜類型進行操作。下面的例子就是:

先要一個復雜類型的類

public class FooBean {
    
private String foo1;
    
private Integer foo2;
    
private Boolean foo3;

    
public FooBean(String foo1, Integer foo2, Boolean foo3) {
        
this.foo1 = foo1;
        
this.foo2 = foo2;
        
this.foo3 = foo3;
}
…. Setter and Getter
}

然后是一個有FooBeanService

public class FooService {
    
public FooBean getFooBean(String foo1,Integer foo2,Boolean foo3){
        
return new FooBean(foo1+"(Remote)",foo2+10,!foo3);
    }
}

很簡單,就是返回一個FooBean,在參數上做了一些手腳J。

然后在server-config.wsdd中加入描述

<service name="FooService" provider="java:RPC">
        
<parameter name="allowedMethods" value="*"/>
        
<parameter name="className" value="org.mstar.ws.FooService"/>
        
<parameter name="scope" value="session"/>
        
<typeMapping encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                     xmlns:ns1
="http://ws.mstar.org"
                     qname
="ns1:FooBean"
                     languageSpecificType
="java:org.mstar.ws.FooBean"
                     serializer
="org.apache.axis.encoding.ser.BeanSerializerFactory"
                     deserializer
="org.apache.axis.encoding.ser.BeanDeserializerFactory"
                     name
="FooBean"/>
</service>

這里多了typeMapping標記用來描述復雜數據類型FooBean。對入復雜類型的序列化可以是自定義的,在serializer deserializer屬性中改。那個xmlns,要留意一下,因為在客戶端生成存根時的AntTask中要寫一些Mapping,來確定那些xmlns映射到哪些package。當然這些也可以在WSDL中找到。而WSDL中的xmlns也是在這里定義的。

下面也各客戶端。由于這次的Service中有復雜類型所以要根據WSDL生成這些復雜類型和Service的存根。取得WSDL的方法可以使用java2WSDL來做,但那樣太麻煩,其實在List頁面中用右鍵-另存為就可以了。然后寫一個ant文件

<?xml version="1.0" encoding="UTF-8"?>
<project default="GenJavaSub" basedir=".">
    
<taskdef name="wsdl2java" classname="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask"/>
    
<target name="GenJavaSub">
        
<property name="output.dir" value="src"/>
        
<property name="generated.dir" value="src/org/mstar/wsclient/generated"/>
        
<property name="wsdl.url" value="wsdl/FooService.wsdl"/>
        
<delete dir="${generated.dir}"/>
        
<mkdir  dir="${generated.dir}"/>
        
<wsdl2java all="true" debug="false" helperGen="true"
            noimports
="false"
            output
="${output.dir}"
            serverside
="false"
            skeletonDeploy
="false"
            typeMappingVersion
="1.1"
            url
="${wsdl.url}"
            verbose
="false"
            noWrapped
="false">
            
<mapping namespace="http://ws.mstar.org" package="org.mstar.wsclient.generated"/>
            
<mapping namespace="http://localhost:8080/ws/services/FooService" package="org.mstar.wsclient.generated"/>
        
</wsdl2java>
    
</target>
</project>

這里上面的東西比較好理解,在下面的mapping中一定要注意,因為每個ServiceComplexType都有自己的xmlns,這里就是把xmlns映射到指定的package 比如 http://ws.mstar.org 映射到org.mstar.wsclient.generated包。這些namespace可以在wsdl文件中找到。運行ant就會看見在src中的org.mstar.wsclient.generated中多了幾個java文件。接下在我們就可以用這幾個類了。還有一點要注意wsdl2java output屬性要指向src,而不是generated文件夾。

客戶端調用就相對容易多了

 

FooServiceService serviceLocator = new FooServiceServiceLocator();
try {
FooService service 
= serviceLocator.getFooService();
    FooBean fooBean 
= service.getFooBean("fooBean"10true);
System.out.println(fooBean);
catch (ServiceException e) {
    e.printStackTrace();
catch (RemoteException e) {
    e.printStackTrace();
}

運行客戶端看看!

 

做簡單的WebService就基本上這樣了。但這樣的Webservice還遠遠不能用戶真正的項目中,還有很多問題沒有解決,比如安全問題,有狀態會話問題等等。這些問題很多可以通過Hanlder來解決,他有點類似于FilterServlet。具體的東西我還有沒有研究,axis在這方面的資料就很少了。研究明白以后再寫一篇吧。

本文源碼:http://www.tkk7.com/Files/mstar/WsLearn.rar