這些天稍微玩了一下
Axis ,以前做 WebServices 都使用 JBuilder ,感覺做 WebServices 如此簡單,現在自己手動做,原來也是如此簡單。高興之余寫一個簡單的初學手冊,就算是學習成果吧。當然對 Axis 理解的還不很深,所以錯誤之處望指點。Axis 是一個實現 WebService 的 Framework , Apache Web Services Project ( http://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
的默認畫面,
這個頁面不是必須的,在真正項目開發中可以把它去掉或換個名字。點擊
List
連接進入已經發布的
WebServices
列表。
開始時應該只有
AdminService
和
Version
。后面兩個就是我們在下面要做的
WebServices
。
第三步,如果上面的一切正常,就可以正式開始做
WebServices
了。首先做一個
Services
實現類。
Calc.java
有兩個方法
plus
和
subtract
。這個
Service
所用到的數據類型都是基本類型。
???? 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 的源碼中有一些 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
進行發布的。在我的例子中是直接把
server-config.wsdd
寫好放到
WEB-INF
下。所以要把
Service
上面那些東西加上,否則系統不能正常運行。
???????? < parameter? name ="allowedMethods" ?value ="*" />
???????? < parameter? name ="className" ?value ="org.mstar.ws.Calc" />
???????? < parameter? name ="scope" ?value ="request" />
???? </ service >
是 Calc 的發布描述。其中 scope 屬性默認是 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();
????????}
這是動態的調用
WebService
的方法,并不需要根據
WSDL
生成客戶端存根。但是對于
Service
中有復合類型的時候就不可以了。下一個例子講的就是如何做客戶端存根。如果這個例子能夠正常運行就可以了。
第五步做一個稍微復雜一點的例子,對于 WebServices 不能僅僅對簡單類型的數據進行操作,也應該能對復雜類型進行操作。下面的例子就是:
先要一個復雜類型的類
???? 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 ,在參數上做了一些手腳 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 標記用來描述復雜數據類型 FooBean 。對入復雜類型的序列化可以是自定義的,在 serializer deserializer 屬性中改。那個 xmlns ,要留意一下,因為在客戶端生成存根時的 AntTask 中要寫一些 Mapping ,來確定那些 xmlns 映射到哪些 package 。當然這些也可以在 WSDL 中找到。而 WSDL 中的 xmlns 也是在這里定義的。
下面也各客戶端。由于這次的
Service
中有復雜類型所以要根據
WSDL
生成這些復雜類型和
Service
的存根。取得
WSDL
的方法可以使用
java2WSDL
來做,但那樣太麻煩,其實在
List
頁面中用右鍵
-
另存為就可以了。然后寫一個
ant
文件
< 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 中一定要注意,因為每個 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 文件夾。
客戶端調用就相對容易多了
?
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
還遠遠不能用戶真正的項目中,還有很多問題沒有解決,比如安全問題,有狀態會話問題等等。這些問題很多可以通過
Hanlder
來解決,他有點類似于
FilterServlet
。具體的東西我還有沒有研究,
axis
在這方面的資料就很少了。研究明白以后再寫一篇吧。