這些天稍微玩了一下
Axis,以前做WebServices都使用JBuilder,感覺做WebServices如此簡單,現(xiàn)在自己手動做,原來也是如此簡單。高興之余寫一個簡單的初學(xué)手冊,就算是學(xué)習(xí)成果吧。當(dāng)然對Axis理解的還不很深,所以錯誤之處望指點。Axis是一個實現(xiàn)WebService的Framework,Apache Web Services Project(http://ws.apache.org )的一個之項目,現(xiàn)在這個項目有很多之項目 Axis(http://ws.apache.org/axis/ )是其中一員,還有XML-RPC(這個也是我比較喜歡的東東J)。
現(xiàn)在Axis主要由兩個版本一個是Axis一個是Axis2。兩個好象有比較多的不同,我這里說的是Axis,過幾天演技一下Axis2,然后再寫一篇吧。
好了現(xiàn)在開始做個WebService 吧:
第一步當(dāng)然是先去Axis主頁下載一個來啦。下Release就行,最新的是1.2.1,source好象沒有打包的只有CVS的。下來以后解壓縮,主要有以下文件夾
Docs 顧名思義,這里放的是文檔,其實Axis的文檔作的很好,我就是按照它的User Guide一步步做下來的。
Lib 運行Axis時要用到的jar包,要完全正常運行還缺兩個mail.jar activation.jar 這兩個是javaMail包,到處都能弄到。
Samples Axis自帶的例子包括很多種應(yīng)用
Webapps Axis是發(fā)布到Servlet Container中的,要把Axis集成到你的項目中,就把這個文件夾里的內(nèi)容合并到你的項目中就行了。
還有一個xmls文件夾,放得是一些可能用到的xml例子。
第二步,建一個項目,Web項目,用Eclipse或者Idea都可以啊。如果你非要用記事本類的東西,我也不攔著你。
把Axis中的Webapps\axis文件夾下的東西統(tǒng)統(tǒng)Copy到你的Web文件夾下。其實有些東西是沒用的,比如classes文件夾里的東西都可以去掉了,還有那幾個jws文件也沒有用。雖然axis最方便的發(fā)布WebServices的方法是把你的.java改成.jws的放到Web發(fā)布文件夾下的根目錄下,但是這種方法沒有什么適用價值。然后運行以下Tomcat(或者其他的Application Server)。然后瀏覽一下你的剛剛發(fā)布的這個項目,如果正常的話就可以看到Axis的默認(rèn)畫面,
這個頁面不是必須的,在真正項目開發(fā)中可以把它去掉或換個名字。點擊List連接進(jìn)入已經(jīng)發(fā)布的WebServices列表。
開始時應(yīng)該只有AdminService和Version。后面兩個就是我們在下面要做的WebServices。
第三步,如果上面的一切正常,就可以正式開始做WebServices了。首先做一個Services實現(xiàn)類。Calc.java有兩個方法plus和subtract。這個Service所用到的數(shù)據(jù)類型都是基本類型。
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的發(fā)布描述文件,作用類似于web.xml。它有自己的格式,但是具體的標(biāo)記是什么樣子的,在Axis的文檔中沒有詳細(xì)的一一列出,只是提到了常用的一些。在axis的源碼中有一些wsdd的XSD文件,如果你用的是IDEA可以把這些XSD映射到uri,這樣編輯器就有提示了。
下面這我們本文中的server-config.wsdd的樣子:
<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進(jìn)行發(fā)布的。在我的例子中是直接把server-config.wsdd寫好放到WEB-INF下。所以要把Service上面那些東西加上,否則系統(tǒng)不能正常運行。
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="org.mstar.ws.Calc"/>
<parameter name="scope" value="request"/>
</service>
是Calc的發(fā)布描述。其中scope屬性默認(rèn)是request所以不寫也可以。其他parameter看名字就知道干什么的了。這樣你在List頁面中就可以查看CalcService的WSDL了。
第四步就是寫客戶端程序了。WSClient.java
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();
}
這是動態(tài)的調(diào)用WebService的方法,并不需要根據(jù)WSDL生成客戶端存根。但是對于Service中有復(fù)合類型的時候就不可以了。下一個例子講的就是如何做客戶端存根。如果這個例子能夠正常運行就可以了。 第五步做一個稍微復(fù)雜一點的例子,對于WebServices不能僅僅對簡單類型的數(shù)據(jù)進(jìn)行操作,也應(yīng)該能對復(fù)雜類型進(jìn)行操作。下面的例子就是: 先要一個復(fù)雜類型的類
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
}
然后是一個有FooBean的Service
public FooBean getFooBean(String foo1,Integer foo2,Boolean foo3){
return new FooBean(foo1+"(Remote)",foo2+10,!foo3);
}
}
很簡單,就是返回一個FooBean,在參數(shù)上做了一些手腳J。
然后在server-config.wsdd中加入描述
<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標(biāo)記用來描述復(fù)雜數(shù)據(jù)類型FooBean。對入復(fù)雜類型的序列化可以是自定義的,在serializer deserializer屬性中改。那個xmlns,要留意一下,因為在客戶端生成存根時的AntTask中要寫一些Mapping,來確定那些xmlns映射到哪些package。當(dāng)然這些也可以在WSDL中找到。而WSDL中的xmlns也是在這里定義的。
下面也各客戶端。由于這次的Service中有復(fù)雜類型所以要根據(jù)WSDL生成這些復(fù)雜類型和Service的存根。取得WSDL的方法可以使用java2WSDL來做,但那樣太麻煩,其實在List頁面中用右鍵-另存為就可以了。然后寫一個ant文件 這里上面的東西比較好理解,在下面的mapping中一定要注意,因為每個Service和ComplexType都有自己的xmlns,這里就是把xmlns映射到指定的package 比如 把http://ws.mstar.org 映射到org.mstar.wsclient.generated包。這些namespace可以在wsdl文件中找到。運行ant就會看見在src中的org.mstar.wsclient.generated中多了幾個java文件。接下在我們就可以用這幾個類了。還有一點要注意wsdl2java 的output屬性要指向src,而不是generated文件夾。 客戶端調(diào)用就相對容易多了
<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>
try {
FooService service = serviceLocator.getFooService();
FooBean fooBean = service.getFooBean("fooBean", 10, true);
System.out.println(fooBean);
} catch (ServiceException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
運行客戶端看看!
做簡單的WebService就基本上這樣了。但這樣的Webservice還遠(yuǎn)遠(yuǎn)不能用戶真正的項目中,還有很多問題沒有解決,比如安全問題,有狀態(tài)會話問題等等。這些問題很多可以通過Hanlder來解決,他有點類似于FilterServlet。具體的東西我還有沒有研究,axis在這方面的資料就很少了。研究明白以后再寫一篇吧。