下面的所有幾乎來自Axis1.1的文檔。但不完全來自文檔,本人做了部分修改,這些修改完全出自國內讀者的需要。
什么是SOAP? SOAP是一個基于XML的用于應用程序之間通信數據編碼的傳輸協議。最初由微軟和Userland Software提出,隨著不斷地完善和改進,SOAP很快被業界廣泛應用,目前完全發布版本是1.1。在其發展過程中,W3C XML標準工作小組積極促成SOAP成為一個真正的開放標準。在寫作此文檔之時,SOAP1.2草案已經發布,1.2對1.1中相對混亂的部分做了改進。
SOAP被廣泛作為新一代跨平臺、跨語言分布計算Web Services的重要部分。
這里太膚淺的說明,請參閱我的整理《一步一步學習SOAP》。
什么是Axis? Axis本質上就是一個SOAP引擎,提供創建服務器端、客戶端和網關SOAP操作的基本框架。Axis目前版本是為Java編寫的,不過為C++的版本正在開發中。
但Axis并不完全是一個SOAP引擎,它還包括:
是一個獨立的SOAP服務器。
是一個嵌入Servlet引擎(例如Tomcat)的服務器。
支持WSDL。
提供轉化WSDL為Java類的工具。
提供例子程序。
提供TCP/IP數據包監視工具。
Axis是第三代Apache SOAP,從2000年起,SOAP v2開發小組開始討論如何讓Axis更加靈活、可配置,以及能夠處理SOAP和來自W3C的各種XML標準。通過不斷地討論和代碼編寫,Axis目前取得了如下成果:
速度提高。 Axis通過基于事件的SAX對XML文檔進行處理,從而在速度和效率上比Apache SOAP有所提高。
靈活性提高。
穩定性提高。
提供面向組件的部署。
提供一個簡潔的傳輸抽象框架。其核心引擎完全于傳輸方式獨立。從而使基于何種協議傳輸的選擇更加靈活。
支持WSDL。包括到處WSDL和客戶代理生成等。
在目前發行1.1版本中有什么東西? SOAP1.1/1.2引擎。
靈活的配置和部署系統。
支持及時自動生成SOAP服務(JWS)。
支持所有的基本數據類型,為自定義串行操作提供類型映射系統。
JavaBean的自動串行操作,包括將自定義屬性類型映射到XML的屬性和元素。
RPC和基于消息的SOAP服務提供者。
從部署好的服務自動生成WSDL。
WSDL2Java工具可以從WSDL描述文件中產生相應的客戶和服務器端SOAP操作框架。
初步提供安全擴展,能夠與Servlet2.2安全集成。
通過HTTP Cookie和與傳輸無關的SOAP頭信息提供會話跟蹤。
初步支持帶附件的SOAP消息。
在EJB方面提供把EJB作為Web服務的訪問途經。
基于Servlet的HTTP傳輸。
基于JMS的傳輸。
獨立的服務器(但需要HTTP 服務器和Servlet容器支持)。
提供客戶端、服務器端相關應用程序的樣例。
Axis的運行需要如下組件包
axis.jar
jaxrpc.jar
saaj.jar
commons-logging.jar
commons-discovery.jar
wsdl4j.jar
符合JAXP-1.1的XML處理器。
一步一步開始用Axis進行Web Services操作
下面給出一段簡單的調用Web Services方法的客戶端代碼(由于原文檔中直接用導入包的方式初學者不易了解那個類在那個包中,所以下面我做了一些簡單的修改,希望能給學習者清晰的思路):
public class TestClient {
public static void main(String[] args) {
try{
String endpoint="http://localhost:8080/axis/SayHello.jws?wsdl";// 調用的web服務的url地址,這里是一個http請求,希望得到的結果是wsdl文檔。
org.apache.axis.client.Service service=new org.apache.axis.client.Service();//建立請求服務框架實例。
/*
* org.apache.axis.client.Service實現JAX-RPC's javax.xml.rpc.Services接口
* 該接口充當產生下面提到的org.apache.axis.client.Call實例
* 的角色。
*/
org.apache.axis.client.Call call=(org.apache.axis.client.Call)service.createCall();//從框架中生成一個維護調用的實例。
/*
* org.apache.axis.client.Call實現了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"));//設置需要調用的函數名稱
String result=(String)call.invoke(new Object[]{"hello!"});
System.out.println(result);
}catch(Exception e){
System.err.println(e.toString());
}
}
}
上面的代碼可能和原文檔中不同,并且在名稱空間、函數多態上會給用戶造成糊涂。不過別急,我在翻譯手冊的同時會加入很多解釋的代碼,如果哪位純粹看不懂可以通過郵件獲取幫助:luopc@edu-edu.com.cn ,郵件主題必須是我提供的文檔名稱。
通過上面的調用代碼,最終生成向服務器請求SOAP信息包,具體XML內容如下:
<?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>
這里我不再多少,細心的朋友請從代碼中找相應的對應信息來進行自己的邏輯理解。至于SOAP協議在我以后的翻譯文檔中會加入進去。
從上面的代碼中我們在調用的時候輸入了參數new Object[]{“hello!”}。然后從生成的SOAP請求包中可以看到自動序列化成<arg0 xsi:type="xsd:string">Hello!</arg0>。你可以看到參數名稱為arg0,類型為xsd:string。其實在Axis客戶端我們可以直接通過具體的方法來設置每一個參數名稱、類型以及返回值類型。如下代碼:
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>
可以作簡單的對比。
也許你會疑惑設置參數名稱和不設置參數名稱有什么區別的疑問。這里作簡單的解釋:
1. 為什么要call.setReturnType(org.apache.axis.Constants.XSD_STRING)?
上面你可以調用或者可以不調用,但當返回結果中沒有標明數據類型時Axis就不知道如何進行數據類型轉換。當然如果返回類型你很清楚并且返回應答SOAP的結果中表明了相應的數據類型你便可以不進行上面的函數調用。
2. 為什么要設置參數名稱和類型?
好了,下在你知道了如何調用Web Services了,下面告訴你如何寫作和發布Web Services。
通過Axis發布Web Services
這里寫作一個簡單的類,然后一步一步進行發布。希望用戶能夠從中理出一些思路來。我在其他的關于Web Services的文章中會專門針對Web Services的通用發布方法。雖然Web Services牽涉到很多復雜的知識,但請大家不要感覺到這些操作簡單,也許你已經有很多疑問,沒關系,記下你的疑問,不斷的投試。
public class SayHello { public String echoString(String hello){ return hello; }}
哈哈,這個類是不是太簡單了。
一步一步來。
Axis提供兩種將Java類發布成Web Services的途徑,即即時快速自動發布和通過配置文件進行發布。我們首先從最容易部署的入手。
JWS----即時部署
簡單說就是將自己寫的Java類源文件按一定的規則Copy到特定的目錄下便可自行被Axis部署。具體步驟如下:
將上面寫的SayHello.java復制到axis目錄下。
改名為SayHello.jws。
注意:在你寫的類中不能有具體包的信息,因為這正是Axis即時部署不支持的。
運行我們前面寫的客戶端進行測試,運行結果是hello!。
???????目前你是否有如下的問題?
???????如果類中用多態的函數將如何處理?
???
??????其實很簡單,我們知道在每次發出調用請求之前代理程序在后臺會直接請求你給的URL,這里是http://localhost:8080/axis/SayHello.jws?wsdl,其實這一請求返回來的是WSDL描述文件,通過具體的描述文件的內容和你輸入的參數的類型比較,最終確定發送什么請求。細節上的解釋需要用戶閱讀SOAP的具體內容。我也會提供相應的權威文當翻譯的。
??????Axis的客戶端默認就是通過HTTP協議傳輸嗎?
??????如何將自己定義的類型進行映射?
??????
??????通過WSDD自定義部署
??????上面的自動部署相當簡單,但當你需要
??????
?????????映射自己的類型時
?????????不需要暴露源代碼時
?????????需要自己的路徑和包管理時
?????????對用戶操作Web Services的事件進行相應時
??????就需要通過WSDD來自定義部署。
??????
??????前面新特性中提到過,Axis是一個很靈活的可配置系統,但在配置之前你得懂得Axis Web Services描述文檔(WSDD)的格式和意義。這里有一個簡單的例子(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>
??????很簡短的描述就會自定義對特定類以Web Services的形式發布。上面的每一個service項都會意味著開通一個相應的可被WSDL文件引用的服務。其內部描述信息將會描述從請求-處理請求-應答需要的所有核心信息。這里provider=”java:RPC”對應著相應的服務類為org.apache.axis.providers.java.RPCProvider。其實你可以通過多種方式提供相應的服務。在關于Axis的架構文章中將會詳細解釋這些內容。
??????上面的各個參數我不再解釋,我想大家一看就明白。
??????
??????下面讓我們在上面的簡單配置上開始一步一步的進行各種高級配置:
??????
??????服務存活范圍
??????
??????Axis提供Session、Request和Application三種可選范圍配置。具體配置標記為:
<service name="MyService"...>
??<parameter name="scope" value="value"/>
??...
</service>
一旦你完成上面的文件。可以寫一個腳本批處理文件來完成部署。批處理文件如下:
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運行需要的組件,所以必須添加到環境中。上面的目錄是我的機器里的目錄,你可以按自己的目錄進行。
???
??????如果部署成功,會輸出<Admin>Done processing</Admin>信息。
但本人這里再強調幾個:
?????????你首先要將你編譯好的類放到axis/web-inf/classes/下面。
?????????可以通過
http://localhost:8080/axis/services/SayHello2來調用。
?????????你可以從我的配置文件中發現了包的概念。
?????????可以用相應的xml文件進行卸載已部署的服務。
????????
???????卸載方法如下:
?????????編寫如下XML文檔
<undeployment xmlns="http://xml.apache.org/axis/wsdd/">
<service name="MyService"/>
</undeployment>
??????把上面的批處理文件中的sayHelloD.wsdd換成該文件便可。
??????繼續關注高級部署
??????如果你想知道自己的某個Web Services被調用多少次,你應該怎么做呢?
Axis提供了相應的跟蹤機制。
??????首先你應該編寫一個滿足一定接口的事件處理類。
??????
??????在配置文件中添加事件處理器信息。
?????我們先看看配置文件中的信息:
<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="訪問該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>
??????上面的綠色部分起到核心的配置信息提供。
??????下面提供一個事件處理類,同樣來自原文檔,但為了更清晰我同樣將所有的類前面加上了包名稱。
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) {
????}
}
??????就這樣,我們對請求跟蹤了。嘻嘻。
??????可以遠程進行管理(不推薦)
??????前面就提到服務類型的問題,在Axis有四種類型的服務,分別為RPC、Document、Wrapped和Message。
Axis下 WebService 的調用問題
人按照網上例子制作一個Sample,遇到一個問題,當用JAVA編寫的客戶端調用返回值類型是int的函數時能正確得到返回值,但是調用返回值類型是String的函數時,不能正確得到返回值。以下為服務器端程序源代碼:
/**
* 此處插入類型描述。
* 創建日期:(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 構造子注解。
*/
public ZxjTest() {
super();
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int getSex()
{
return sex;
}
public List getItems()
{
return items;
}
}
客戶端調用的源代碼:
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[] {});
出錯的就是最后一句。如果這一句改成調用返回值是Int的函數,就不會出錯。請問,這是Axis的配置的問題嗎?如果是,該如何改?
應該不大可能是Axis的配置的問題,你調用的是
public String getName()
{
return name;
}
因此返回的是String,這個是沒有錯誤的!
你的程序我在自己機器上運行的很好!不管調用哪個方法,都一樣輸出。你可以把String ret = (String) call.invoke(new Object[] {});改成Object ret = (Object)call.invoke(new Object[] {});這樣的話你就不用去類型轉換了!
或者如果你要類型轉換的話,也可以這樣做:
1,如果調用返回類型是string的方法 那么最后一句應該為
String ret = (String) call.invoke(new Object[] {});
2,如果調用返回類型是int的方法,那么最后一句應該為
Integer ret = (Integer)call.invoke(new Object[] {});
還有要注意的是在利用客戶端進行測試的時候,確保服務器已經啟動,否則會出錯。如果你是采用http://myserver:7001/ZxjTest.jws的話,你的web-inf下確保沒有
server-config.wsdd,否則也會出錯。
還有一個很簡單的錯誤,可能僅僅只是WebLogic配置的問題。最好重建一個應用并重新配置。