SOAP(簡(jiǎn)單對(duì)象訪問協(xié)議)是一種利用XML編碼數(shù)據(jù)的有線協(xié)議。它是同類協(xié)議中要求最低的一個(gè)規(guī)范,只定義了有線協(xié)議所要求的最關(guān)鍵的部分,有意地忽略了垃圾收集、對(duì)象激活等方面的細(xì)節(jié)。
SOAP對(duì)于Java開發(fā)者來說尤其重要,因?yàn)樗屍脚_(tái)無關(guān)和可移植的Java程序更容易協(xié)同操作,使得Java的寶貴特性進(jìn)一步增值。事實(shí)上,如果Java 2平臺(tái)企業(yè)版(J2EE)的下一個(gè)版本讓SOAP成為一種必須遵循的有線協(xié)議,規(guī)定所有遵從J2EE規(guī)范的應(yīng)用服務(wù)器都必須支持SOAP協(xié)議,我也不會(huì)感到奇怪。不過就現(xiàn)在來說,我想我的猜想應(yīng)該暫停了。 這個(gè)系列的文章總共四篇,這是第二篇。在這里,我要介紹的是Apache SOAP實(shí)現(xiàn)。 一、Apache SOAP概述 Apache SOAP,即Apache Software Foundation對(duì)SOAP規(guī)范的實(shí)現(xiàn),建立在IBM的SOAP4J的基礎(chǔ)上。和所有其他Apache工程類似,Apache SOAP源代碼開放,按照Apache許可協(xié)議發(fā)行。我覺得這是當(dāng)前最好的SOAP實(shí)現(xiàn)之一。然而,雖然Apache SOAP遵從SOAP規(guī)范1.1版本,但它缺乏SOAP 1.1所包含的某些功能。 1.1、下載和安裝Apache SOAP 如前所述,Apache SOAP可以免費(fèi)下載(參見“參考資源”中提供的下載鏈接)。我為我的Windows NT便攜機(jī)下載了soap-bin-2.0.zip文件,該文件包含Apache SOAP 2.0,這是寫作本文時(shí)的最新版本。安裝Apache SOAP可謂輕而易舉,共包含如下三個(gè)簡(jiǎn)單的步驟: 1. 解開下載所得文件的ZIP壓縮:解開壓縮之后就得到了一個(gè)soap-2_0子目錄。我把ZIP文件的內(nèi)容解壓縮到E盤的根目錄下,因此有了一個(gè)包含Apache SOAP的E:\soap-2_0目錄。 2. 配置Web環(huán)境:要有一個(gè)支持Servlet和JSP的Web服務(wù)器。至此,你可能遇到下面兩種情況之一: o 情況1:已經(jīng)有一個(gè)支持Servlet和JSP的Web服務(wù)器,而且你覺得配置這個(gè)服務(wù)器沒有問題。在這種情況下,請(qǐng)配置服務(wù)器,使得瀏覽器可以訪問http://localhost:8080/apache-soap/,打開soap-2_0 \webapps\soap\目錄下面的index.html文件。 3. 設(shè)置Web服務(wù)器classpath:Apache SOAP要求有1.1.2版本或更高的Apache Xerces(Java),它支持DOM(文檔對(duì)象模型)Level 2規(guī)范,支持名稱空間。我使用1.2版本的Xerces,即Apache網(wǎng)站的Xerces-J-bin.1.2.0.zip。解開這個(gè)壓縮文件,得到xerces-1_2_0子目錄。和前面一樣,我把解壓縮得到的文件放到了E:盤的根目錄之下。你應(yīng)該配置Web服務(wù)器,使它能夠用xerces.jar(它在xerces-1_2_0子目錄下)進(jìn)行所有XML解析——而不是用服務(wù)器附帶的庫(kù)或jar解析XML。例如,Tomcat附帶了一個(gè)XML解析器(jakarta-tomcat\lib\xml.jar),支持DOM Level 1接口。即使你把xerces.jar放入了classpath,Tomcat下運(yùn)行的Java代碼也可能找錯(cuò)接口,因?yàn)樵谟脕韱?dòng)Tomcat的Shell腳本/批命令文件中,xerces.jar被放到了classpath的最后。因此,必須編輯jakarta-tomcat\bin目錄下的tomcat.bat(對(duì)于Unix,則是tomcat.sh),把xerces.jar放到classpath的前面。下面是我在jakarta-tomcat\bin\tomcat.bat文件中所作的修改: set CLASSPATH=E:\xerces-1_2_0\xerces.jar;%CLASSPATH%;%cp% 如果你在第二個(gè)步驟中屬于情況2,也必須配置服務(wù)器,使它能夠使用xerces.jar。 不管你屬于哪一種情況,除了配置xerces.jar之外,你還必須配置Web服務(wù)器的classpath使它能夠使用soap-2_0\lib\目錄下的soap.jar。
1.2、檢查安裝是否成功
現(xiàn)在,啟動(dòng)Web服務(wù)器,用瀏覽器打開http://localhost:8080/apache-soap/admin,驗(yàn)證安裝是否成功。這時(shí),你應(yīng)該看到所示的管理屏幕。 二、實(shí)例:HelloWorld 現(xiàn)在你已經(jīng)設(shè)置好了Apache SOAP,我們來實(shí)際使用一下,構(gòu)造一個(gè)簡(jiǎn)單的HelloWorld應(yīng)用。在SOAP術(shù)語(yǔ)中,應(yīng)用稱為服務(wù)。一般地,創(chuàng)建服務(wù)分兩個(gè)步驟,這兩個(gè)步驟可能由同一個(gè)人或組織實(shí)施,也可能不是。第一個(gè)步驟是在選定的語(yǔ)言中定義和實(shí)現(xiàn)服務(wù)本身,這里我們選擇Java語(yǔ)言。第二個(gè)步驟是創(chuàng)建實(shí)際調(diào)用服務(wù)的客戶程序。首先我們來看HelloWorld服務(wù)。 2.1、HelloWorld服務(wù) 我在第一篇文章中討論了一個(gè)用SOAP實(shí)現(xiàn)的HelloWorld服務(wù)實(shí)例。這個(gè)服務(wù)要求輸入一個(gè)用戶名字,返回一個(gè)定制的Hello消息給調(diào)用者。下面的代碼顯示了HelloWorld服務(wù)的完整Java代碼。 package hello; 這就是全部的代碼嗎?如果這是真的話,實(shí)在太簡(jiǎn)單了! Apache SOAP使創(chuàng)建服務(wù)變得極其簡(jiǎn)單。服務(wù)主要由業(yè)務(wù)邏輯構(gòu)成,不管服務(wù)以哪種方式提供給外界使用,業(yè)務(wù)邏輯代碼都是必須編寫的。換句話說,服務(wù)不會(huì)和任何SOAP相關(guān)的代碼混合,因?yàn)锳pache SOAP底層體系——它由rpcrouter Servlet和soap.jar構(gòu)成——幫助我們完成了所有錯(cuò)綜復(fù)雜的工作。我們來簡(jiǎn)要地探討一下這些錯(cuò)綜復(fù)雜的工作究竟包含些什么,例如,Apache SOAP如何處理HTTP上的遠(yuǎn)程過程調(diào)用(RPC)請(qǐng)求?理解這一點(diǎn)將給創(chuàng)建客戶程序帶來方便(不錯(cuò),是客戶程序)。 在Apache SOAP中,org.apache.soap.rpc包支持在SOAP上進(jìn)行RPC調(diào)用。Apache RPC支持的關(guān)鍵在于對(duì)象ID。所有的Apache SOAP服務(wù)必須有一個(gè)唯一的ID,它被視為服務(wù)的對(duì)象ID。眾所周知,唯一性是一個(gè)相對(duì)的概念;在Apache SOAP中,對(duì)象ID的唯一性相對(duì)于服務(wù)所部署的Apache SOAP服務(wù)器而言。也就是說,部署在不同Apache SOAP服務(wù)器上的兩個(gè)服務(wù)可能有同樣的對(duì)象ID。 想要使用服務(wù)的客戶程序設(shè)置一個(gè)org.apache.soap.rpc.Call對(duì)象,指定目標(biāo)服務(wù)的對(duì)象ID、待調(diào)用方法的名字以及提供給方法的參數(shù)(如果有的話)。設(shè)置好Call對(duì)象之后,客戶程序調(diào)用它的invoke()方法。invoke()方法需要兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)執(zhí)行rpcrouter Servlet的URL,如http://localhost:8080/apache-soap/servlet/rpcrouter;第二個(gè)參數(shù)是SOAPAction頭(請(qǐng)參考本系列的第一篇文章,了解SOAPAction頭的重要性和可能的取值)。 invoke()方法把Call對(duì)象轉(zhuǎn)換成XML SOAP請(qǐng)求(類似第一篇文章所提供的示例),把請(qǐng)求發(fā)送給第一個(gè)參數(shù)中的URL所指定的rpcrouter Servlet。當(dāng)Servlet返回應(yīng)答,invoke()方法返回一個(gè)org.apache.soap.rpc.Response對(duì)象,這個(gè)對(duì)象包含了服務(wù)的應(yīng)答(如果有的話)或錯(cuò)誤信息(如果出現(xiàn)了錯(cuò)誤)。HTTP規(guī)定每一個(gè)請(qǐng)求都必須有一個(gè)應(yīng)答;因此,即使服務(wù)本身不返回任何東西,rpcrouter Servlet總是會(huì)返回一些內(nèi)容。因此,invoke()方法總是返回一個(gè)Response對(duì)象。 在服務(wù)端,Apache SOAP服務(wù)器(也就是rpcrouter Servlet)接收客戶程序發(fā)送的SOAP請(qǐng)求,重新構(gòu)造出Call對(duì)象。Servlet使用重新構(gòu)造得到的Call對(duì)象中的對(duì)象ID在服務(wù)管理器中確定具體的對(duì)象。 接下來,Servlet在已經(jīng)確定的對(duì)象上檢驗(yàn)被調(diào)用方法的名字并調(diào)用方法。完成后,Servlet串行化該調(diào)用的返回值,在HTTP應(yīng)答中把它發(fā)送給客戶程序。
從上述討論中,我們可以發(fā)現(xiàn)一個(gè)有趣的問題:Apache SOAP如何知道串行化某種給定數(shù)據(jù)類型的方法?Apache SOAP通過一個(gè)類型注冊(cè)器(org.apache.soap.encoding.SOAPMappingRegistry),以及通過所有裝配器(marshaller)和反裝配器(marshaller)分別必須實(shí)現(xiàn)的串行化(org.apache.soap.util.xml.Serializer)和反串行化(org.apache.soap.util.xml.Deserialization)接口,實(shí)現(xiàn)Java數(shù)據(jù)類型和XML之間的裝配和反裝配。Apache SOAP提供了許多實(shí)現(xiàn)這些接口的內(nèi)建的裝配器和反裝配器。例如,我們可以用org.apache.soap.encoding.soapenc.BeanSerializer類裝配和反裝配JavaBean。本文后面我將介紹如何使用這個(gè)類。
對(duì)于Java基本數(shù)據(jù)類型(int,long,double,等等)及其對(duì)應(yīng)的對(duì)象化形式(Integer,Long,Double,等等)來說,它們的串行化器和反串行化器已經(jīng)預(yù)先在類型映射注冊(cè)器中注冊(cè)。因此,對(duì)于客戶程序來說,用Java基本數(shù)據(jù)類型及其對(duì)象形式作為方法參數(shù)是無縫的。然而,如果服務(wù)所要求的參數(shù)是一個(gè)JavaBean,則必須手工在類型映射注冊(cè)器中注冊(cè)BeanSerializer。服務(wù)永遠(yuǎn)不會(huì)做任何額外的工作,最后客戶程序的負(fù)擔(dān)總是較多。在這個(gè)系列的第四篇文章中,我將介紹用動(dòng)態(tài)代理構(gòu)造的框架,它將使創(chuàng)建客戶程序和創(chuàng)建服務(wù)程序一樣簡(jiǎn)單。 2.2、部署HelloWorld服務(wù) 部署Apache SOAP服務(wù)有兩種方法:使用Web界面的管理工具,或通過命令行進(jìn)行部署。所有這兩種方法都可以部署服務(wù),使服務(wù)可被客戶程序訪問。 ■ 使用管理工具部署服務(wù) 要使用管理工具,用瀏覽器打開http://localhost:8080/apache-soap/admin。瀏覽器將顯示出圖一所示的界面。點(diǎn)擊窗口左邊的Deploy按鈕,一個(gè)帶有許多輸入框的窗口就會(huì)出現(xiàn)。并非所有的輸入框現(xiàn)在都要用到。我將在用到這些輸入框的時(shí)候介紹它們的含義。由于本文無需用到所有的輸入框,所以我們將忽略部分輸入框的含義。但不用擔(dān)心,到第三篇文章結(jié)束時(shí),我將介紹完所有的輸入框。 ID輸入框用來設(shè)置對(duì)象ID;如前所述,SOAP基礎(chǔ)設(shè)施利用對(duì)象ID把RPC請(qǐng)求綁定到SOAP服務(wù)。我在前面已經(jīng)提到,所有Apache SOAP服務(wù)必須有一個(gè)對(duì)象ID,這個(gè)對(duì)象ID在該服務(wù)器上部署的所有服務(wù)之間唯一。我通常使用“urn:<UniqueServiceID>”格式,其中UniqueServiceID是服務(wù)的唯一對(duì)象ID。在本例中,把ID設(shè)置成“urn:hello”。 Scope輸入框用來定義響應(yīng)調(diào)用請(qǐng)求的服務(wù)實(shí)例的生存范圍和時(shí)間。Scope可以是下列值之一: · page:服務(wù)實(shí)例一直有效,直至應(yīng)答發(fā)送完畢或把請(qǐng)求傳遞給了另一個(gè)頁(yè)面——如果使用標(biāo)準(zhǔn)的部署機(jī)制,向前傳遞請(qǐng)求不太可能發(fā)生。 Scope的值對(duì)安全有著重要的影響,記住這一點(diǎn)很重要。page和request值確保了連續(xù)調(diào)用之間的隔離。在另一個(gè)極端,application值意味著所有SOAP的用戶共享服務(wù)實(shí)例。細(xì)心的讀者可能已經(jīng)注意到,JSP的標(biāo)記同樣要用到這些值。事實(shí)上,rpcrouter Servlet曾經(jīng)就是一個(gè)JSP頁(yè)面,這也許是這些值被選用的原因。在本例中,我們把Scope的值設(shè)置成application。 在Methods輸入框中,輸入用空白字符分隔的方法名字,這些方法名字指示出當(dāng)前部署的服務(wù)上允許調(diào)用的方法。我們的服務(wù)示例只支持一個(gè)方法,即sayHelloTo()。 把Provider Type設(shè)置成Java。它意味著服務(wù)用Java實(shí)現(xiàn),而且你必須為Apache SOAP提供服務(wù)完整的類名。這個(gè)任務(wù)在Provider Class輸入框完成,我們把它設(shè)置成hello.HelloServer。由于sayHelloTo()方法不是靜態(tài)的,保持Static輸入框原來的值,即no。 現(xiàn)在滾動(dòng)到頁(yè)面的下方,點(diǎn)擊表單下面的Deploy按鈕(不是左邊的Deploy按鈕)。要驗(yàn)證服務(wù)已經(jīng)部署完畢,點(diǎn)擊左邊的List按鈕,這時(shí)列表所顯示的服務(wù)中應(yīng)該包含一個(gè)urn:Hello服務(wù)。
從命令行部署服務(wù)
部署服務(wù)除了可以用Web界面的管理工具,還可以用命令行Java工具org.apache.soap.server.ServiceManagerClient,它是一個(gè)Apache SOAP附帶的類。這個(gè)類要求有兩個(gè)必不可少的參數(shù),一個(gè)指向Apache SOAP路由Servlet(即rpcrouter)的URL,以及一個(gè)動(dòng)作。這個(gè)動(dòng)作可以是以下四者之一:deploy,undeploy,list,或query。根據(jù)指定動(dòng)作的不同,有時(shí)候還要提供額外的參數(shù)。例如,如果動(dòng)作是deploy,則必須提供XML部署描述器文件的名字。部署描述器文件應(yīng)該包含Apache SOAP服務(wù)器成功部署服務(wù)所需要的全部信息。例如,描述HelloWorld部署細(xì)節(jié)的部署XML文件可以如下: <isd:service xmlns:isd="http://xml./xml-soap/deployment" 上述XML代碼所包含的信息和我們?cè)赪eb界面的管理工具中所輸入的信息一樣。接下來,輸入下面的命令,從命令行部署HelloWorld服務(wù): java org.apache.soap.server.ServiceManagerClient DeploymentDescriptor.xml是上面顯示的描述部署信息的XML文件名字。要驗(yàn)證服務(wù)是否部署成功,輸入以下命令: java org.apache.soap.server.ServiceManagerClient 這時(shí),我們應(yīng)該看到和DeploymentDescriptor.xml文件內(nèi)一樣的XML。 2.3、HelloWorld客戶程序 編寫客戶程序要比編寫HelloWorld服務(wù)復(fù)雜得多。不過,你應(yīng)該不會(huì)對(duì)此感到奇怪,因?yàn)榍懊嬉呀?jīng)提到,客戶程序(至少)必須負(fù)責(zé)設(shè)置Call對(duì)象,這需要不少工作。順便說一下,本系列文章的第四篇將介紹一個(gè)框架,這個(gè)框架以Java 2 1.3版新引入的動(dòng)態(tài)代理類為基礎(chǔ),使創(chuàng)建客戶程序和創(chuàng)建服務(wù)一樣簡(jiǎn)單。 Listing 1顯示了完整的客戶程序。接下來我們一步一步地仔細(xì)看看這個(gè)程序。這個(gè)程序需要一個(gè)必不可少的參數(shù):程序要向他說Hello信息的用戶名字。 Listing 1: Client.java public class Client // 檢查應(yīng)答
if( !resp.generatedFault() ) { Parameter ret = resp.getReturnValue(); Object value = ret.getValue(); System.out.println(value); } else { Fault fault = resp.getFault(); System.err.println("Generated fault: "); System.out.println (" Fault Code = " + fault.getFaultCode()); System.out.println (" Fault String = " + fault.getFaultString()); } } catch(Exception e) { e.printStackTrace(); } } } 客戶程序首先設(shè)置Call對(duì)象,它需要如下信息:
· 被調(diào)用服務(wù)的對(duì)象ID,它通過Call對(duì)象的setTargetObjectURI()方法設(shè)置。本例的對(duì)象ID是urn:Hello。 下面的代碼片斷顯示了客戶程序創(chuàng)建Call對(duì)象的過程: // 構(gòu)造Call對(duì)象 現(xiàn)在,該是實(shí)際調(diào)用HelloWorld遠(yuǎn)程服務(wù)所提供方法的時(shí)候了。為此,客戶程序調(diào)用了Call對(duì)象的invoke()方法,這個(gè)方法返回一個(gè)org.apache.soap.rpc.Response對(duì)象,如下所示: // 發(fā)出調(diào)用 接下來,客戶程序檢查Response對(duì)象。如果方法調(diào)用過程中出現(xiàn)了錯(cuò)誤,generateFault()方法返回一個(gè)true值,客戶程序提取并顯示實(shí)際的錯(cuò)誤信息: Fault fault = resp.getFault(); 如果方法調(diào)用成功,則客戶程序提取并顯示Hello信息: // 檢查應(yīng)答
if( !resp.generatedFault() ) { Parameter ret = resp.getReturnValue(); Object value = ret.getValue(); System.out.println(value); } 三、帶有JavaBean的HelloWorld實(shí)例
如前所述,Apache SOAP提供了許多預(yù)先構(gòu)造的串行化和反串行化方法,其中包括為利用Java Vector、Enumeration、數(shù)組、JavaBean作為參數(shù)和返回值而提供的串行化器和反串行化器。在這一部分,我將修改HelloWorld服務(wù),通過一個(gè)JavaBean傳入接收Hello信息的用戶名。 3.1、HelloWorld服務(wù) 改寫后的HelloWorld服務(wù)完整代碼如下: package hello; 服務(wù)的代碼仍舊很簡(jiǎn)單,仍舊類似于不用JavaBean時(shí)的HelloWorld服務(wù)。不過,這意味著最復(fù)雜的工作都轉(zhuǎn)移到了客戶端。事實(shí)上,這個(gè)版本的服務(wù)與以前版本的唯一差別在于,現(xiàn)在出現(xiàn)了一個(gè)重載的sayHelloTo()方法。上面的代碼中重載后的方法用粗體字顯示。 重載的方法需要一個(gè)對(duì)Name JavaBean的引用。Name JavaBean的定義如下: package hello; 3.2、部署服務(wù) 部署一個(gè)使用了JavaBean的服務(wù)時(shí),需要為Apache SOAP服務(wù)器提供一些額外的信息。因此,現(xiàn)在部署服務(wù)的過程稍微復(fù)雜一點(diǎn)。 使用管理工具部署服務(wù) 要使用管理工具部署這個(gè)新版的HelloWorld服務(wù),首先按照前面所介紹的步驟進(jìn)行,但這一次不要點(diǎn)擊Deploy按鈕?,F(xiàn)在,在Number of Mappings輸入框輸入1,它表示我們將給出一個(gè)映射(即Name JavaBean)的信息。緊接Mappings之下有一個(gè)表格,我們要用到這個(gè)表格的第一行。保留Encoding Style的值為SOAP,把NameSpace URI設(shè)置成對(duì)象的ID:在本例中,它是urn:Hello。接下來,把Local Part和Java Type輸入框設(shè)置成Name JavaBean的完整名字,即hello.Name。最后,把Java to XML Serializer和XML to Java Deserializer輸入框設(shè)置成org.apache.soap.encoding.soapenc.BeanSerializer,這是一個(gè)實(shí)現(xiàn)了Serializer和Deserializer接口的類,用來串行化和反串行化Name JavaBean。如果你用到了更多的JavaBean(比如還有一個(gè)Address Bean),則應(yīng)該在這個(gè)表格中輸入其他Bean的信息,同時(shí)還應(yīng)該更新Number of Mappings輸入框的值,使之反映出表格中實(shí)際被使用的行數(shù)。
從命令行部署服務(wù) 要從命令行進(jìn)行部署,我們只需修改作為命令行參數(shù)傳入的XML部署描述器文件。修改后的XML文件如下所示: <isd:service xmlns:isd="http://xml./xml-soap/deployment" id="urn:Hello"> 正如在前一個(gè)例子中,這些XML代碼所包含的信息和通過Web界面的管理工具所提供的信息一樣。 3.3、HelloWorld客戶程序 和第一個(gè)例子一樣,客戶程序更復(fù)雜,也更令人感興趣。這里我不再仔細(xì)分析整個(gè)客戶程序,而是介紹兩個(gè)客戶程序版本的不同之處。由于調(diào)用方法的一個(gè)參數(shù)(在本例中,它是唯一的參數(shù))是一個(gè)JavaBean,所以必須手工設(shè)置一個(gè)類型映射注冊(cè)項(xiàng)。這個(gè)任務(wù)通過如下步驟完成:先創(chuàng)建org.apache.soap.encoding.SOAPMappingRegistry類的一個(gè)實(shí)例,然后調(diào)用它的mapTypes()方法。正如mapTypes()方法名字所預(yù)示的,它用來注冊(cè)一個(gè)以前未知的類型,比如定制的JavaBean。mapTypes()方法的參數(shù)包括要使用的編碼方式、限定的JavaBean名字、類型的完整類名、串行化器和反串行化器。在本例中,執(zhí)行串行化任務(wù)的是標(biāo)準(zhǔn)的Bean串行化器。限定的JavaBean名字包含一個(gè)元素的名字,包括它所屬的名稱空間。在本例中,Name JavaBean的限定名字由名稱空間URI(urn:Hello)和本地名字(hello.Name)結(jié)合構(gòu)成。請(qǐng)看下面的代碼片斷: // 創(chuàng)建類型映射注冊(cè)器 接下來,客戶程序必須告訴Call對(duì)象使用新的注冊(cè)器而不是默認(rèn)的注冊(cè)器。為此,我們要調(diào)用Call對(duì)象的setSOAPMappingRegistry()方法,如下所示: call.setSOAPMappingRegistry(smr); 手工設(shè)置好類型映射注冊(cè)器之后,接下來還必須為Call對(duì)象設(shè)置參數(shù)。這一步驟可以按前面介紹的方法完成,不同之處在于,現(xiàn)在我們不再用字符串類型的名字作為參數(shù),而是用JavaBean作為參數(shù),如下所示: // 設(shè)置調(diào)用參數(shù) 客戶程序剩下的部分和原來的版本一樣。Listing 3顯示了完整的客戶程序代碼: Listing 3: Client2.java public class Client2 // 檢查應(yīng)答
if( !resp.generatedFault() ) { Parameter ret = resp.getReturnValue(); Object value = ret.getValue(); System.out.println(value); } else { Fault fault = resp.getFault(); System.err.println("Generated fault: "); System.out.println (" Fault Code = " + fault.getFaultCode()); System.out.println (" Fault String = " + fault.getFaultString()); } } catch(Exception e) { e.printStackTrace(); } } } 四、編譯和運(yùn)行程序
現(xiàn)在整個(gè)程序的開發(fā)工作已經(jīng)完成,該是運(yùn)行它的時(shí)候了。不過,我們首先要編譯服務(wù)程序和客戶程序。 創(chuàng)建一個(gè)hello目錄,把Client1.java、Client2.java和HelloServer.java復(fù)制到這個(gè)目錄。我把hello目錄放到了Apache SOAP的示例目錄(即E:\soap-2_0\samples)之下。編譯程序時(shí),classpath中只需包含hello目錄的父目錄(即E:\soap-2_0\samples)、soap.jar和xerces.jar。我用下面的批命令編譯程序: set CLASSPATH=E:\soap-2_0\samples\;E:\soap-2_0\lib\soap.jar;E:\xerces-1_2_0\xerces.jar 注意:從hello目錄執(zhí)行這個(gè)批命令文件。 要使用這個(gè)服務(wù),除了部署它之外,還需要修改Web服務(wù)器的classpath,確保Web服務(wù)能夠找到hello.HelloServer類——對(duì)于本例,這是指把E:\soap-2_0\samples加入到Web服務(wù)器的classpath。對(duì)classpath進(jìn)行必要的修改之后,重新啟動(dòng)Web服務(wù)器。接下來就可以運(yùn)行客戶程序了。下面是我運(yùn)行hello.Client的批命令文件: set CLASSPATH=E:\soap-2_0\samples\;E:\soap-2_0\lib\soap.jar;E:\xerces-1_2_0\xerces.jar 這里的classpath和編譯程序時(shí)用的classpath相同。 最后,運(yùn)行hello.Client2的批命令文件可以如下: set CLASSPATH=E:\soap-2_0\samples\;E:\soap-2_0\lib\soap.jar;E:\xerces-1_2_0\xerces.jar 觀察Web服務(wù)器的控制臺(tái)窗口,看看在運(yùn)行兩個(gè)不同的客戶程序時(shí),HelloWorld服務(wù)的哪些方法正在被調(diào)用。 結(jié)束語(yǔ) 在這篇文章中,我介紹了如何用Apache SOAP實(shí)現(xiàn)來創(chuàng)建簡(jiǎn)單的基于SOAP的服務(wù)。在SOAP實(shí)現(xiàn)方面,另一個(gè)重要的競(jìng)爭(zhēng)者是Microsoft。遺憾的是,“純”Java開發(fā)者在使用Microsoft實(shí)現(xiàn)的時(shí)候會(huì)有一段艱苦的時(shí)光,因?yàn)樗膶?shí)現(xiàn)包含了COM對(duì)象。 在下一篇文章中,我將介紹Apache SOAP支持的另一種創(chuàng)建服務(wù)的方式:使用JavaScript之類的腳本語(yǔ)言,而不是Java。另外,我還要介紹一個(gè)很不錯(cuò)的JavaScript引擎,即Rhino |
|