下面的所有幾乎來(lái)自Axis1.1的文檔。但不完全來(lái)自文檔,本人做了部分修改,這些修改完全出自國(guó)內(nèi)讀者的需要。
什么是SOAP? SOAP是一個(gè)基于XML的用于應(yīng)用程序之間通信數(shù)據(jù)編碼的傳輸協(xié)議。最初由微軟和Userland Software提出,隨著不斷地完善和改進(jìn),SOAP很快被業(yè)界廣泛應(yīng)用,目前完全發(fā)布版本是1.1。在其發(fā)展過(guò)程中,W3C XML標(biāo)準(zhǔn)工作小組積極促成SOAP成為一個(gè)真正的開(kāi)放標(biāo)準(zhǔn)。在寫(xiě)作此文檔之時(shí),SOAP1.2草案已經(jīng)發(fā)布,1.2對(duì)1.1中相對(duì)混亂的部分做了改進(jìn)。
SOAP被廣泛作為新一代跨平臺(tái)、跨語(yǔ)言分布計(jì)算Web Services的重要部分。
這里太膚淺的說(shuō)明,請(qǐng)參閱我的整理《一步一步學(xué)習(xí)SOAP》。
什么是Axis? Axis本質(zhì)上就是一個(gè)SOAP引擎,提供創(chuàng)建服務(wù)器端、客戶(hù)端和網(wǎng)關(guān)SOAP操作的基本框架。Axis目前版本是為Java編寫(xiě)的,不過(guò)為C++的版本正在開(kāi)發(fā)中。
但Axis并不完全是一個(gè)SOAP引擎,它還包括:
是一個(gè)獨(dú)立的SOAP服務(wù)器。
是一個(gè)嵌入Servlet引擎(例如Tomcat)的服務(wù)器。
支持WSDL。
提供轉(zhuǎn)化WSDL為Java類(lèi)的工具。
提供例子程序。
提供TCP/IP數(shù)據(jù)包監(jiān)視工具。
Axis是第三代Apache SOAP,從2000年起,SOAP v2開(kāi)發(fā)小組開(kāi)始討論如何讓Axis更加靈活、可配置,以及能夠處理SOAP和來(lái)自W3C的各種XML標(biāo)準(zhǔn)。通過(guò)不斷地討論和代碼編寫(xiě),Axis目前取得了如下成果:
速度提高。 Axis通過(guò)基于事件的SAX對(duì)XML文檔進(jìn)行處理,從而在速度和效率上比Apache SOAP有所提高。
靈活性提高。
穩(wěn)定性提高。
提供面向組件的部署。
提供一個(gè)簡(jiǎn)潔的傳輸抽象框架。其核心引擎完全于傳輸方式獨(dú)立。從而使基于何種協(xié)議傳輸?shù)倪x擇更加靈活。
支持WSDL。包括到處WSDL和客戶(hù)代理生成等。
在目前發(fā)行1.1版本中有什么東西? SOAP1.1/1.2引擎。
靈活的配置和部署系統(tǒng)。
支持及時(shí)自動(dòng)生成SOAP服務(wù)(JWS)。
支持所有的基本數(shù)據(jù)類(lèi)型,為自定義串行操作提供類(lèi)型映射系統(tǒng)。
JavaBean的自動(dòng)串行操作,包括將自定義屬性類(lèi)型映射到XML的屬性和元素。
RPC和基于消息的SOAP服務(wù)提供者。
從部署好的服務(wù)自動(dòng)生成WSDL。
WSDL2Java工具可以從WSDL描述文件中產(chǎn)生相應(yīng)的客戶(hù)和服務(wù)器端SOAP操作框架。
初步提供安全擴(kuò)展,能夠與Servlet2.2安全集成。
通過(guò)HTTP Cookie和與傳輸無(wú)關(guān)的SOAP頭信息提供會(huì)話(huà)跟蹤。
初步支持帶附件的SOAP消息。
在EJB方面提供把EJB作為Web服務(wù)的訪問(wèn)途經(jīng)。
基于Servlet的HTTP傳輸。
基于JMS的傳輸。
獨(dú)立的服務(wù)器(但需要HTTP 服務(wù)器和Servlet容器支持)。
提供客戶(hù)端、服務(wù)器端相關(guān)應(yīng)用程序的樣例。
Axis的運(yùn)行需要如下組件包
axis.jar
jaxrpc.jar
saaj.jar
commons-logging.jar
commons-discovery.jar
wsdl4j.jar
符合JAXP-1.1的XML處理器。
一步一步開(kāi)始用Axis進(jìn)行Web Services操作
下面給出一段簡(jiǎn)單的調(diào)用Web Services方法的客戶(hù)端代碼(由于原文檔中直接用導(dǎo)入包的方式初學(xué)者不易了解那個(gè)類(lèi)在那個(gè)包中,所以下面我做了一些簡(jiǎn)單的修改,希望能給學(xué)習(xí)者清晰的思路):
public class TestClient {
public static void main(String[] args) {
try{
String endpoint="http://localhost:8080/axis/SayHello.jws?wsdl";// 調(diào)用的web服務(wù)的url地址,這里是一個(gè)http請(qǐng)求,希望得到的結(jié)果是wsdl文檔。
org.apache.axis.client.Service service=new org.apache.axis.client.Service();//建立請(qǐng)求服務(wù)框架實(shí)例。
/*
* org.apache.axis.client.Service實(shí)現(xiàn)JAX-RPC's javax.xml.rpc.Services接口
* 該接口充當(dāng)產(chǎn)生下面提到的org.apache.axis.client.Call實(shí)例
* 的角色。
*/
org.apache.axis.client.Call call=(org.apache.axis.client.Call)service.createCall();//從框架中生成一個(gè)維護(hù)調(diào)用的實(shí)例。
/*
* org.apache.axis.client.Call實(shí)現(xiàn)了JAX-RPC's javax.xml.rpc.Call接口。
*/
call.setTargetEndpointAddress(new java.net.URL(endpoint));
call.setOperationName(new javax.xml.namespace.QName("http://www.edu-edu.com.cn/luopc/ws","echoString"));//設(shè)置需要調(diào)用的函數(shù)名稱(chēng)
String result=(String)call.invoke(new Object[]{"hello!"});
System.out.println(result);
}catch(Exception e){
System.err.println(e.toString());
}
}
}
上面的代碼可能和原文檔中不同,并且在名稱(chēng)空間、函數(shù)多態(tài)上會(huì)給用戶(hù)造成糊涂。不過(guò)別急,我在翻譯手冊(cè)的同時(shí)會(huì)加入很多解釋的代碼,如果哪位純粹看不懂可以通過(guò)郵件獲取幫助:luopc@edu-edu.com.cn ,郵件主題必須是我提供的文檔名稱(chēng)。
通過(guò)上面的調(diào)用代碼,最終生成向服務(wù)器請(qǐng)求SOAP信息包,具體XML內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <ns1:echoString xmlns:ns1="http://www.edu-edu.com.cn/luopc/ws"> <arg0 xsi:type="xsd:string">Hello!</arg0> </ns1:echoString> </SOAP-ENV:Body></SOAP-ENV:Envelope>
這里我不再多少,細(xì)心的朋友請(qǐng)從代碼中找相應(yīng)的對(duì)應(yīng)信息來(lái)進(jìn)行自己的邏輯理解。至于SOAP協(xié)議在我以后的翻譯文檔中會(huì)加入進(jìn)去。
從上面的代碼中我們?cè)谡{(diào)用的時(shí)候輸入了參數(shù)new Object[]{“hello!”}。然后從生成的SOAP請(qǐng)求包中可以看到自動(dòng)序列化成<arg0 xsi:type="xsd:string">Hello!</arg0>。你可以看到參數(shù)名稱(chēng)為arg0,類(lèi)型為xsd:string。其實(shí)在Axis客戶(hù)端我們可以直接通過(guò)具體的方法來(lái)設(shè)置每一個(gè)參數(shù)名稱(chēng)、類(lèi)型以及返回值類(lèi)型。如下代碼:
call.addParameter("testParam", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN);call.setReturnType(org.apache.axis.Constants.XSD_STRING);
加入上面的代碼之后生成如下的SOAP信息:
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <ns1:echoString xmlns:ns1="http://www.edu-edu.com.cn/luopc/ws"> <testParam xsi:type="xsd:string">Hello!</testParam> </ns1:echoString> </SOAP-ENV:Body></SOAP-ENV:Envelope>
可以作簡(jiǎn)單的對(duì)比。
也許你會(huì)疑惑設(shè)置參數(shù)名稱(chēng)和不設(shè)置參數(shù)名稱(chēng)有什么區(qū)別的疑問(wèn)。這里作簡(jiǎn)單的解釋?zhuān)?br />
1. 為什么要call.setReturnType(org.apache.axis.Constants.XSD_STRING)?
上面你可以調(diào)用或者可以不調(diào)用,但當(dāng)返回結(jié)果中沒(méi)有標(biāo)明數(shù)據(jù)類(lèi)型時(shí)Axis就不知道如何進(jìn)行數(shù)據(jù)類(lèi)型轉(zhuǎn)換。當(dāng)然如果返回類(lèi)型你很清楚并且返回應(yīng)答SOAP的結(jié)果中表明了相應(yīng)的數(shù)據(jù)類(lèi)型你便可以不進(jìn)行上面的函數(shù)調(diào)用。
2. 為什么要設(shè)置參數(shù)名稱(chēng)和類(lèi)型?
好了,下在你知道了如何調(diào)用Web Services了,下面告訴你如何寫(xiě)作和發(fā)布Web Services。
通過(guò)Axis發(fā)布Web Services
這里寫(xiě)作一個(gè)簡(jiǎn)單的類(lèi),然后一步一步進(jìn)行發(fā)布。希望用戶(hù)能夠從中理出一些思路來(lái)。我在其他的關(guān)于Web Services的文章中會(huì)專(zhuān)門(mén)針對(duì)Web Services的通用發(fā)布方法。雖然Web Services牽涉到很多復(fù)雜的知識(shí),但請(qǐng)大家不要感覺(jué)到這些操作簡(jiǎn)單,也許你已經(jīng)有很多疑問(wèn),沒(méi)關(guān)系,記下你的疑問(wèn),不斷的投試。
public class SayHello { public String echoString(String hello){ return hello; }}
哈哈,這個(gè)類(lèi)是不是太簡(jiǎn)單了。
一步一步來(lái)。
Axis提供兩種將Java類(lèi)發(fā)布成Web Services的途徑,即即時(shí)快速自動(dòng)發(fā)布和通過(guò)配置文件進(jìn)行發(fā)布。我們首先從最容易部署的入手。
JWS----即時(shí)部署
簡(jiǎn)單說(shuō)就是將自己寫(xiě)的Java類(lèi)源文件按一定的規(guī)則Copy到特定的目錄下便可自行被Axis部署。具體步驟如下:
將上面寫(xiě)的SayHello.java復(fù)制到axis目錄下。
改名為SayHello.jws。
注意:在你寫(xiě)的類(lèi)中不能有具體包的信息,因?yàn)檫@正是Axis即時(shí)部署不支持的。
運(yùn)行我們前面寫(xiě)的客戶(hù)端進(jìn)行測(cè)試,運(yùn)行結(jié)果是hello!。
???????目前你是否有如下的問(wèn)題?
???????如果類(lèi)中用多態(tài)的函數(shù)將如何處理?
???
??????其實(shí)很簡(jiǎn)單,我們知道在每次發(fā)出調(diào)用請(qǐng)求之前代理程序在后臺(tái)會(huì)直接請(qǐng)求你給的URL,這里是http://localhost:8080/axis/SayHello.jws?wsdl,其實(shí)這一請(qǐng)求返回來(lái)的是WSDL描述文件,通過(guò)具體的描述文件的內(nèi)容和你輸入的參數(shù)的類(lèi)型比較,最終確定發(fā)送什么請(qǐng)求。細(xì)節(jié)上的解釋需要用戶(hù)閱讀SOAP的具體內(nèi)容。我也會(huì)提供相應(yīng)的權(quán)威文當(dāng)翻譯的。
??????Axis的客戶(hù)端默認(rèn)就是通過(guò)HTTP協(xié)議傳輸嗎?
??????如何將自己定義的類(lèi)型進(jìn)行映射?
??????
??????通過(guò)WSDD自定義部署
??????上面的自動(dòng)部署相當(dāng)簡(jiǎn)單,但當(dāng)你需要
??????
?????????映射自己的類(lèi)型時(shí)
?????????不需要暴露源代碼時(shí)
?????????需要自己的路徑和包管理時(shí)
?????????對(duì)用戶(hù)操作Web Services的事件進(jìn)行相應(yīng)時(shí)
??????就需要通過(guò)WSDD來(lái)自定義部署。
??????
??????前面新特性中提到過(guò),Axis是一個(gè)很靈活的可配置系統(tǒng),但在配置之前你得懂得Axis Web Services描述文檔(WSDD)的格式和意義。這里有一個(gè)簡(jiǎn)單的例子(deploy.wsdd):
????
???<deployment xmlns="http://xml.apache.org/axis/wsdd/"
?????? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
???? <service name="SayHello2" provider="java:RPC">
??????<parameter name="className" value="luopc.ws.SayHello"/>
??????<parameter name="allowedMethods" value="*"/>
???? </service>
????</deployment>
??????很簡(jiǎn)短的描述就會(huì)自定義對(duì)特定類(lèi)以Web Services的形式發(fā)布。上面的每一個(gè)service項(xiàng)都會(huì)意味著開(kāi)通一個(gè)相應(yīng)的可被WSDL文件引用的服務(wù)。其內(nèi)部描述信息將會(huì)描述從請(qǐng)求-處理請(qǐng)求-應(yīng)答需要的所有核心信息。這里provider=”java:RPC”對(duì)應(yīng)著相應(yīng)的服務(wù)類(lèi)為org.apache.axis.providers.java.RPCProvider。其實(shí)你可以通過(guò)多種方式提供相應(yīng)的服務(wù)。在關(guān)于Axis的架構(gòu)文章中將會(huì)詳細(xì)解釋這些內(nèi)容。
??????上面的各個(gè)參數(shù)我不再解釋?zhuān)蚁氪蠹乙豢淳兔靼住?br />??????
??????下面讓我們?cè)谏厦娴暮?jiǎn)單配置上開(kāi)始一步一步的進(jìn)行各種高級(jí)配置:
??????
??????服務(wù)存活范圍
??????
??????Axis提供Session、Request和Application三種可選范圍配置。具體配置標(biāo)記為:
<service name="MyService"...>
??<parameter name="scope" value="value"/>
??...
</service>
一旦你完成上面的文件。可以寫(xiě)一個(gè)腳本批處理文件來(lái)完成部署。批處理文件如下:
java -classpath F:\resource\tools\axis-1_1\lib\axis.jar;F:\resource\tools\axis-1_1\lib\commons-discovery.jar;F:\resource\tools\axis-1_1\lib\jaxrpc.jar;F:\resource\tools\axis-1_1\lib\commons-logging.jar;F:\resource\tools\axis-1_1\lib\log4j-1.2.8.jar;F:\resource\tools\axis-1_1\lib\wsdl4j.jar;F:\resource\tools\axis-1_1\lib\j2ee.jar org.apache.axis.client.AdminClient sayHelloD.wsdd。
??????上面的一些jar文件都是Axis運(yùn)行需要的組件,所以必須添加到環(huán)境中。上面的目錄是我的機(jī)器里的目錄,你可以按自己的目錄進(jìn)行。
???
??????如果部署成功,會(huì)輸出<Admin>Done processing</Admin>信息。
但本人這里再?gòu)?qiáng)調(diào)幾個(gè):
?????????你首先要將你編譯好的類(lèi)放到axis/web-inf/classes/下面。
?????????可以通過(guò)
http://localhost:8080/axis/services/SayHello2來(lái)調(diào)用。
?????????你可以從我的配置文件中發(fā)現(xiàn)了包的概念。
?????????可以用相應(yīng)的xml文件進(jìn)行卸載已部署的服務(wù)。
????????
???????卸載方法如下:
?????????編寫(xiě)如下XML文檔
<undeployment xmlns="http://xml.apache.org/axis/wsdd/">
<service name="MyService"/>
</undeployment>
??????把上面的批處理文件中的sayHelloD.wsdd換成該文件便可。
??????繼續(xù)關(guān)注高級(jí)部署
??????如果你想知道自己的某個(gè)Web Services被調(diào)用多少次,你應(yīng)該怎么做呢?
Axis提供了相應(yīng)的跟蹤機(jī)制。
??????首先你應(yīng)該編寫(xiě)一個(gè)滿(mǎn)足一定接口的事件處理類(lèi)。
??????
??????在配置文件中添加事件處理器信息。
?????我們先看看配置文件中的信息:
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
?? xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
??<!-- define the logging handler configuration -->
<handler name="track" type="java:samples.userguide.example4.LogHandler">
??<parameter name="parameter1" value="訪問(wèn)該services"/>
</handler>
<!-- define the service, using the log handler we just defined -->
<service name="SayHello2" provider="java:RPC">
??<requestFlow>
?? <handler type="track"/>
??</requestFlow>
??<parameter name="className" value="luopc.ws.SayHello"/>
??<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
??????上面的綠色部分起到核心的配置信息提供。
??????下面提供一個(gè)事件處理類(lèi),同樣來(lái)自原文檔,但為了更清晰我同樣將所有的類(lèi)前面加上了包名稱(chēng)。
package luopc.ws;
/**
* @author luopc
*/
public class EventHandler extends org.apache.axis.handlers.BasicHandler{
????public void invoke(org.apache.axis.MessageContext mtxt){
????????String param=(String)getOption("parameter1");
????????System.out.println(param);
????}
????public static void main(String[] args) {
????}
}
??????就這樣,我們對(duì)請(qǐng)求跟蹤了。嘻嘻。
??????可以遠(yuǎn)程進(jìn)行管理(不推薦)
??????前面就提到服務(wù)類(lèi)型的問(wèn)題,在Axis有四種類(lèi)型的服務(wù),分別為RPC、Document、Wrapped和Message。
Axis下 WebService 的調(diào)用問(wèn)題
人按照網(wǎng)上例子制作一個(gè)Sample,遇到一個(gè)問(wèn)題,當(dāng)用JAVA編寫(xiě)的客戶(hù)端調(diào)用返回值類(lèi)型是int的函數(shù)時(shí)能正確得到返回值,但是調(diào)用返回值類(lèi)型是String的函數(shù)時(shí),不能正確得到返回值。以下為服務(wù)器端程序源代碼:
/**
* 此處插入類(lèi)型描述。
* 創(chuàng)建日期:(2005-10-27 10:32:48)
* @author:Administrator
*/
import java.util.*;
public class ZxjTest {
protected String name="gaga";
protected int age=20;
protected int sex=22;
protected List items=new ArrayList();
/**
* ZxjTest 構(gòu)造子注解。
*/
public ZxjTest() {
super();
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int getSex()
{
return sex;
}
public List getItems()
{
return items;
}
}
客戶(hù)端調(diào)用的源代碼:
String endpoint = "http://myserver:7001/ZxjTest.jws";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(new java.net.URL(endpoint));
call.setOperationName("getName");
String ret = (String) call.invoke(new Object[] {});
出錯(cuò)的就是最后一句。如果這一句改成調(diào)用返回值是Int的函數(shù),就不會(huì)出錯(cuò)。請(qǐng)問(wèn),這是Axis的配置的問(wèn)題嗎?如果是,該如何改?
應(yīng)該不大可能是Axis的配置的問(wèn)題,你調(diào)用的是
public String getName()
{
return name;
}
因此返回的是String,這個(gè)是沒(méi)有錯(cuò)誤的!
你的程序我在自己機(jī)器上運(yùn)行的很好!不管調(diào)用哪個(gè)方法,都一樣輸出。你可以把String ret = (String) call.invoke(new Object[] {});改成Object ret = (Object)call.invoke(new Object[] {});這樣的話(huà)你就不用去類(lèi)型轉(zhuǎn)換了!
或者如果你要類(lèi)型轉(zhuǎn)換的話(huà),也可以這樣做:
1,如果調(diào)用返回類(lèi)型是string的方法 那么最后一句應(yīng)該為
String ret = (String) call.invoke(new Object[] {});
2,如果調(diào)用返回類(lèi)型是int的方法,那么最后一句應(yīng)該為
Integer ret = (Integer)call.invoke(new Object[] {});
還有要注意的是在利用客戶(hù)端進(jìn)行測(cè)試的時(shí)候,確保服務(wù)器已經(jīng)啟動(dòng),否則會(huì)出錯(cuò)。如果你是采用http://myserver:7001/ZxjTest.jws的話(huà),你的web-inf下確保沒(méi)有
server-config.wsdd,否則也會(huì)出錯(cuò)。
還有一個(gè)很簡(jiǎn)單的錯(cuò)誤,可能僅僅只是WebLogic配置的問(wèn)題。最好重建一個(gè)應(yīng)用并重新配置。