摘要:
XFire是一個免費的開源SOAP框架,它不僅可以極大方便地實現(xiàn)這樣一個環(huán)境,并且可以提供許多Web Services規(guī)范中高級特征,這些特征在多數(shù)的商業(yè)或者開源工具都沒有提供。你要恰當?shù)睦斫膺@些單詞:great ease and simplicity(非常輕松和簡單)。你將會看到使用XFire創(chuàng)建Web Services是多么的簡單。
將你的POJO方法發(fā)布為Web Services摘要問一下.Net的開發(fā)者,開發(fā)Web Services有多困難?他們會被你的問題逗樂,他們會告訴你所有步驟花費的時間不會超過一分鐘。再問一下Java開發(fā)者,運氣好的話你會碰到上面的回答,一般情況下,你會得到一個完全不同的答案。從Web Services引入到Java中至今已經(jīng)5年了,然而它仍然被認為是一門較新的技術(shù),一個主要的原因就是使用Java實現(xiàn)Web Services太困難了。現(xiàn)在,隨著新一代Web Services引擎XFire的發(fā)布,這種情況將會發(fā)生明顯的變化。使用XFire,你可以把Java類方法發(fā)布為Web Services而不需要編寫額外的代碼。在這篇文章中,你將會看到XFire使Web Services開發(fā)變得多么容易和簡單。
Web Services使我們能夠在網(wǎng)絡上建立分布式系統(tǒng),應用程序組件可以通過任何平臺、任何語言和任何方式訪問。無論應用程序如何開發(fā),使用了什么語言,以及運行在什么操作系統(tǒng)平臺上,只要它作為Web Service,并且為協(xié)同解決問題而設計,那么你的應用程序,以任何語言開發(fā)或在任何平臺上,都可以利用它的服務。這是Web Service的主要概念。
為了實現(xiàn)Web Services的平臺無關(guān)性和實現(xiàn)訪問獨立性,軟件行業(yè)需要遵循一些作為標準的技術(shù)。其中一些包括:
---XML:在Web Services環(huán)境中各層之間進行傳遞的默認數(shù)據(jù)格式。
-- -SOAP:封裝和交換信息的默認協(xié)議。第一次被提出時,它是只取Simple Object Access Protocol(簡單對象訪問協(xié)議)的首字母。但是現(xiàn)在SOAP更多被認為是一個特定的名詞,以它自己而命名,同樣很多人認為這是用詞不當:SOAP實際上不是用來訪問對象的。另外,它也不再簡單。
---WSDL(Web Services Description Language,Web Services描述語言):描述Web Services的語言。盡管基于XML并且可以被人理解,WSDL主要是由機器處理,由客戶端程序讀取和理解。
下面的高級層次圖表,基于WWW協(xié)會發(fā)布的“Web Services Architecture”(Web Services架構(gòu))文檔,顯示了這些技術(shù)在實際的工作環(huán)境中是如何發(fā)揮作用:

這個流程圖顯示了Web Services中的核心技術(shù)是如何工作的。
這里,Provider是提供服務的應用程序組件,Requester是使用服務的客戶端程序。很多其他技術(shù)也會參與到交互中,但是這個圖只顯示了在Web Services環(huán)境中必需的核心技術(shù)組件。
XFire 是一個免費的開源SOAP框架,它不僅可以極大方便地實現(xiàn)這樣一個環(huán)境,并且可以提供許多Web Services規(guī)范中高級特征,這些特征在多數(shù)的商業(yè)或者開源工具都沒有提供。你要恰當?shù)睦斫膺@些單詞:great ease and simplicity(非常輕松和簡單)。你將會看到使用XFire創(chuàng)建Web Services是多么的簡單。
假如你的Web應用有一個Java類,并且你想把它的一個方法發(fā)布為Web Services,當使用XFire時,你不需要編寫一行額外的Java代碼。只需要編輯發(fā)布描述符,然后你就會得到一個Web Services。是的,它相當?shù)睾唵巍N覀儊砜匆粋€例子。
一個簡單的Java類我們的例子是一個銀行業(yè)應用程序,服務器是運行在J2SE1.4.2_07下的Apache Tomcat5.5.7。假定你已經(jīng)了解如何使用Java編寫Web應用程序,并知道應該如何把它部署到Apache Tomcat服務器上。我們的Web應用程序非常簡單;它只做一件事——將資金從一個賬戶轉(zhuǎn)到另外一個賬戶上。一個普通的Java類 BankingService包含了一個叫做transferFunds()的方法來為我們完成這項工作。它需要四個輸入?yún)?shù):
1、????????String fromAccount
2、????????String toAccount
3、????????double amount
4、????????String currency
代碼如下:
package?com.mybank.xfire.example;

import?java.text.NumberFormat;
import?java.text.DecimalFormat;


/**?*//**?XFire?WebServices?sample?implementation?class.?
*/

public?class?BankingService?implements?IBankingService?
{
????
????//Default?constructor.

????public?BankingService()
{????
????}
????

????/**?*//**?Transfers?fund?from?one?account?to?another.
????*/
????public?String?transferFunds(

????????String?fromAccount,?String?toAccount,?double?amount,?String?currency)
{
????????
????????String?statusMessage?=?"";
??????????????????????
????????//Call?business?objects?and?other?components?to?get?the?job?done.
????????//Then?create?a?status?message?and?return.

????????try?
{
????????????NumberFormat?formatter?=?new?DecimalFormat("###,###,###,###.00");???????
????????????statusMessage?=?"COMPLETED:?"?+?currency?+?"?"?+?formatter.format(amount)+?
????????????"?was?successfully?transferred?from?A/C#?"?+?fromAccount?+?"?to?A/C#?"?+?toAccount;

????????}?catch?(Exception?e)
{
????????????statusMessage?=?"BankingService.transferFunds():?EXCEPTION:?"?+?e.toString();
????????}
????????return?statusMessage;
????}
????
}在這里你看到了什么異常的東西了嗎?或許沒有,除了默認的構(gòu)造函數(shù),類型是public。這是必須的。否則,XFire不能夠初始化這個類。
因為使用接口的設計是一個好的實踐,所以我們的Java類也實現(xiàn)了一個稱為IBankingService的接口。代碼十分簡單:
?
package?com.mybank.xfire.example;


public?interface?IBankingService?
{??

????public?String?transferFunds(
????????String?fromAccount,?String?toAccount,?double?amount,?String?currency);
????????
}
在實際實現(xiàn)中,這樣一個方法可能包括各種類型的復雜調(diào)用、查詢和處理操作。但是我們的示例代碼已經(jīng)最小化了,以至于我們可以集中精力在主要目標上:把這個方法發(fā)布為Web Services。
你可以看到BankingService是一個普通的Java類,沒有任何代碼告訴我們它將會在Web Services中使用。好的,這里我們不需要增加任何東西。我們所有的工作都在部署描述符里完成。
Web應用的部署描述符
在Java 中,Web應用程序通常需要至少一個部署描述符(叫做web.xml)對其進行配置。XFire本身是一個基于servlet的應用程序。因此,我們需要增加必要的引用到描述符文件中。然后我們還必須配置將要創(chuàng)建的Web Services。我們使用一個稱為services.xml的新文件來完成這件事。
web.xml
首先,修改web.xml。我們需要增加下面的XFire servlet相關(guān)的條目:
???<servlet>
????????<servlet-name>XFireServlet</servlet-name>
????????<display-name>XFire?Servlet</display-name>
????????<servlet-class>org.codehaus.xfire.transport.http.XfireConfigurableServlet
?????????</servlet-class>
????</servlet>

????<servlet-mapping>
????????<servlet-name>XFireServlet</servlet-name>
????????<url-pattern>/servlet/XFireServlet/*</url-pattern>
????</servlet-mapping>
????
????<servlet-mapping>
????????<servlet-name>XFireServlet</servlet-name>
????????<url-pattern>/services/*</url-pattern>
????</servlet-mapping>
services.xml
現(xiàn)在我們不得不說一下我們的Web Services的由什么組成的了。這由一個叫做services.xml的文件完成,它存放在META-INF/xfire目錄下,而這整個目錄放在 WEB-INF/classes文件夾中,它在Web應用程序的標準類路徑中。這里是services.xml中的基本配置條目:
<beans?xmlns="http://xfire.codehaus.org/config/1.0">
??
??<service>
????<name>Banking</name>
????<namespace>mybank</namespace>
????<serviceClass>com.mybank.xfire.example.IBankingService</serviceClass>
????<implementationClass>com.mybank.xfire.example.BankingService</implementationClass>
??</service>??
??
</beans>讓我們看看這里都包含了什么內(nèi)容。Web Services的定義包含在<service>元素中,它還含有一些子元素。第一個子元素是<name>,它可以是你提供任何的合法名字。這將會被客戶端程序和其它需要定位你的服務的組件用到。例如,在服務準備好以后,你將在瀏覽器上使用這個名字來查看WSDL。
下一個子元素是<namespace>。任何合法的XML名字都是可以的。<namespace>用來唯一標識你的服務的各個參數(shù)。
< serviceClass>元素包含了Java類的名字,它指定了方法簽名。在我們的例子中,它是接口IBankingService。如果 Java類沒有實現(xiàn)任何接口,你就需要把類的名字放在這里。在你的Java類或者接口中可能有幾個方法。只需要一個入口把它們?nèi)堪l(fā)布為Web Services。
<implementationClass>保存了實現(xiàn)方法的Java類名。這是一個可選元素。如果上一個元素<serviceClass>包含了一個接口,那么相應的實現(xiàn)類必須在這里指定。
就是這樣。我們的Web Services配置完成了。
XFire和其它類庫現(xiàn)在是最后一步了,需要得到所有必需的類庫。我們怎樣得到它們呢?去XFire網(wǎng)站,下載xfire-distribution-1.0.zip,然后解壓到一個本地文件夾。復制下面的jar文件和它的庫文件夾到WEB-INF/lib中:
?????????activation-1.0.2.jar
?????????commons-codec-1.3.jar
?????????commons-httpclient-3.0.jar
?????????commons-logging-1.0.4.jar
?????????jaxen-1.1-beta-8.jar
?????????jdom-1.0.jar
?????????log4j-1.2.x.jar
?????????mail-1.3.3_01.jar
?????????spring-1.2.x.jar
?????????stax-api-1.0.jar
?????????wsdl4j-1.5.2.jar
?????????wstx-asl-2.9.jar
?????????xbean-2.1.0.jar
?????????xbean-spring-2.2.jar
?????????xfire-all-1.0.jar
?????????XmlSchema-1.0.jar
一切妥當。我們來部署和啟動應用程序。為了部署示例應用,只需要復制websvc.war到Apache Tomcat的webapps文件夾中,再等待幾秒鐘。它將會自動啟動。這個應用的全部源代碼也包含在這個war文件中。我們的程序已經(jīng)準備作為一個 Web Service了。
我們?nèi)绾沃繵eb Service正在工作呢?為了了解Web Service是否正在工作,我們需要測試。首先,我們測試來看WSDL是否可用。我們在瀏覽器中輸入URL。哪個URL?因為我們的應用程序的war文件是websvc.war,并且在services.xml中給出的服務名是Banking,WSDL的URL應該是:http: //localhost:8080/websvc/services/Banking?wsdl。
請注意:URL的第一部分,例如, http://localhost:8080,可能會根據(jù)你的應用服務器不同而不同。無論怎樣,當你輸入URL后,將會看到一個XML文檔,它的根元素是 <wsdl:definitions>。這個文檔叫做服務的WSDL。如果你看到了,這就是你的應用作為Web Service已經(jīng)可用的第一個證明。
但是這個測試是不夠的。可能會發(fā)生這種情況,可以看到WSDL,但是從客戶端程序可能會訪問不到服務。因此為了核實服務是否可以訪問了,我們必須使用一個客戶端進行服務的實際調(diào)用來進行一個真正的測試。
開發(fā)一個客戶端你可以使用任何的SOAP工具創(chuàng)建客戶端,例如,.Net或者Apache Axis,有很多種方法:使用從WSDL產(chǎn)生的stubs,使用動態(tài)代理,等等。在例子中,我們使用一個動態(tài)代理,以一個簡單的Servlet形式,叫做 WsClient.java。為了保持代碼兩最小,所有在屏幕顯示的元素都放在了doGet()方法中。對Web Service的實際調(diào)用由callWebService()方法完成,它相當?shù)睾唵巍:拖旅娴念愃疲?br />
?

???/**//*?Call?the?Web?service
????*
????*/
????public?String?callWebService(
????????String?fromAccount,?String?toAccount,?double?amount,?String?currency)?

????????throws?MalformedURLException,?Exception?
{
????????
????????//Create?a?metadata?of?the?service??????
????????Service?serviceModel?=?new?ObjectServiceFactory().create(IBankingService.class);????????
????????log.debug("callSoapServiceLocal():?got?service?model."?);
???
????????//Create?a?proxy?for?the?deployed?service
????????XFire?xfire?=?XFireFactory.newInstance().getXFire();
????????XFireProxyFactory?factory?=?new?XFireProxyFactory(xfire);??????
????
????????String?serviceUrl?=?"http://localhost:8080/websvc/services/Banking";
????????
????????IBankingService?client?=?null;

????????try?
{
????????????client?=?(IBankingService)?factory.create(serviceModel,?serviceUrl);

????????}?catch?(MalformedURLException?e)?
{
????????????log.error("WsClient.callWebService():?EXCEPTION:?"?+?e.toString());
????????}????
???????????????
????????//Invoke?the?service
????????String?serviceResponse?=?"";

????????try?
{?
????????????serviceResponse?=?client.transferFunds(fromAccount,?toAccount,?amount,?currency);

???????}?catch?(Exception?e)
{
????????????log.error("WsClient.callWebService():?EXCEPTION:?"?+?e.toString());?????????????????
????????????serviceResponse?=?e.toString();
????????}????????
????????log.debug("WsClient.callWebService():?status="?+?serviceResponse);??????????????

????????//Return?the?response
????????return?serviceResponse;
????}??
這個代碼是如何工作的呢?我來解釋一下:首先,我們創(chuàng)建一個服務模型,它包含服務的說明——換句話說,就是服務的元數(shù)據(jù)。我們使用XFire的ObjectServiceFactory從IBankingService.class接口創(chuàng)建這個模型。
接著,為XFire獲得一個代理工廠對象,它包含了常規(guī)的代碼,也相當?shù)睾唵魏鸵锥_@一步中沒有任何特定應用的東西。從這個proxyFactory,使用服務模型和服務端點URL(用來獲得WSDL),我們可以得到一個服務的本地代理。
就是它了。這個代理就是實際的客戶端。現(xiàn)在,我們可以調(diào)用它的transferFunds()方法來得到我們需要的Web Service。
一旦示例應用發(fā)布并啟動,就可以嘗試servlet URL:
http://localhost:8080/websvc/ws。
這個Servlet使用默認參數(shù)來調(diào)用Web Service和顯示接收到的響應。頁面的最后兩行應該讀取:
Response Received
COMPLETED: CDN$ 500.00 was successfully transferred from A/C# 11111-01234 to A/C# 99999-05678
現(xiàn)在你可以確定Web Service已經(jīng)發(fā)布并且在運行中了。
為了嘗試不同的輸入值,你可以使用完整的URL,例如:
http://localhost:8080/websvc/ws?from=11-2345&to=77-9876&amt=250.00&cur=EUR。
基本的Web Services開發(fā)步驟清單
這個清單總結(jié)了將一個Java方法發(fā)布為Web Service所必須的步驟:
1、????????檢查Java類的方法和默認構(gòu)造函數(shù)確保為public
2、????????增加XFire servlet相關(guān)條目到web.xml中
3、????????創(chuàng)建services.xml,把它放到WEB-INF/classes/META-INF/xfire目錄下
4、????????增加XFire和第三方包到你的Web應用的WEB-INF/lib文件夾中
這就是所有需要的步驟,是的,相當簡單。
XFire的其他高級特性
XFire的使用可能比較簡單,但是在特性和功能性上,它卻占據(jù)著領(lǐng)導者的位置。下面是它的高級特性:
-- -本地數(shù)據(jù)綁定支持POJOs(plain-old Java objects)、XMLBeans、JAXB(Java Architecture for XML Binding)、Castor等等。數(shù)據(jù)綁定說明了Web Services的XML請求和映射到Java對象的XML響應。
---使用StAX(Streaming API for XML)處理XML文檔。同DOM的基于樹和SAX的事件驅(qū)動的處理方式相比,StAX使用了拉(pull)機制,它使處理更快速,內(nèi)存效率更高。
---支持多種傳輸協(xié)議,如HTTP、JMS(Java Message Service)和JVM內(nèi)部傳輸。
---嵌入式,這是XFire的核心功能之一。你可以把這個SOAP引擎嵌入到你的應用中,完全隱藏所有XFire特定引用,同樣所有配置都是程序驅(qū)動。
---豐富的API,它使XFire可高度自定義,允許開發(fā)者在不同的階段截獲請求,并且對他們進行處理。
-- -兼容最新的標準例如SOAP1.1(沒有加密遠程工程調(diào)用,或者RPC)和1.2、WSDL1.1、the Web Services Interoperability Organization’s Basic Profile 1.0、Web Services Addressing和WS-Security。
最重要的是,XFire屬于新一代Web Services引擎。不僅僅是營銷用語,“新一代”有一些重要的意義。Apache Axis是第一代Java語言的Web Services引擎,已經(jīng)成為了所有后來工具的參考標準。在過去的幾年里,Axis以及這些其它的工具已經(jīng)在很多生產(chǎn)環(huán)境中進行了實地測試。從中得出的一個關(guān)鍵的問題就是Web Services并不最適合RPC類型的通信。對于性能和效率,面向文檔的消息形式是最好的方式。但是Apache Axis和很多其他的Web Services引擎都被設計成了面向RPC的(盡管它們支持文檔形式)。現(xiàn)在,整個行業(yè)正在開發(fā)新一代的SOAP引擎,設計為面向文檔的。Apache 已經(jīng)宣布結(jié)束舊版本的Axis引擎開發(fā),現(xiàn)在專注于Axis2,現(xiàn)在它的預發(fā)布版本是0.95。XFire在今年的2月份發(fā)布了它的第一個產(chǎn)品版本(1.0)。它的下一個版本(1.1)僅僅在幾個星期之后就進行了發(fā)布。
性能
Web Services需要消耗很多資源,但是性能方面它們不是那么引人注目。XFire打破了這種趨勢。它消耗更少的內(nèi)存(部分因為 StAX的使用),但是表現(xiàn)卻比多數(shù)可比較的SOAP引擎出色。你可以在資源中提供的鏈接中看到比較的結(jié)果。
此外,XFire還提供了各種方法來進一步優(yōu)化性能。一個方法是使用JVM內(nèi)置傳輸(in-JVM transport)。如果你知道Web Services和客戶端運行在同一個JVM上,你可以選擇使用本地傳輸,它可以大幅提升性能。在示例中的客戶端代碼,看以下指定服務端點URL的這行:
String serviceUrl = "http://localhost:8080/websvc/services/Banking";
替換為
String serviceUrl = "xfire.local://Banking";
你會看到性能上的明顯提高,因為它繞過了整個網(wǎng)絡層。
局限性
XFire有些重要的局限性你應該清楚:
-- -開發(fā)Web Services的好的實踐是從WSDL開始。大部分的SOAP引擎提供了從WSDL生成服務stub的工具。XFire也提供了這樣一個工具。但是它是基于注釋(annotations-based)的,因此需要J2SE5.0。對于仍堅持使用J2SE1.4.x的人來說,它不是一個令人拍手叫好的工具,因為我們有很多其他方式來編寫客戶端,一個就是文章中演示的方式。
---附件支持,它將會包含在未來發(fā)行的版本中。
---易于學習的用戶向?qū)АFire團隊在這個方面還有很多工作需要做。
結(jié)論
Java 當前的趨勢是簡化技術(shù)的使用。因此,我們正看到一波基于POJO的開發(fā)成就。同時,面向服務架構(gòu)(SOA,Services-oriented architecture)和Web Services已經(jīng)變成了當前行業(yè)的熱點話題。XFire正是在這種情況下產(chǎn)生的。它能夠使POJO發(fā)布為最少的Web Services,而只需要付出最小化的努力。從而,它使希望使用這項技術(shù)的初級開發(fā)者的學習曲線變得平緩。同時,由于它兼容最新標準和提供了豐富的 API,XFire為高級用戶提供了更多的大好機會。
關(guān)于作者
Shahid Ahmed是一個軟件架構(gòu)師,如今在美國一家主要的無線運行商作咨詢工作。他在22年前開始開發(fā)軟件,當時Kaypro、Osborne、Apple II和TRS-80s統(tǒng)治著整個地球。在過去的7年里,他更多的精力集中于Java。他現(xiàn)在的工作包括SOA、Web Services、消息、POJO、Spring、Ajax等等。Shahid是一個軟件熱衷者,每天都在奮斗以求在技術(shù)的迷戀和社會生活的熱愛之間尋找平衡點。
資源:
Matrix Java社區(qū):http://www.matrix.org.cn
下載本文中的示例應用:http://www.javaworld.com/javaworld/jw-05-2006/xfire/jw-0501-xfire.zip
Web Services架構(gòu):http://www.w3.org/TR/ws-arch/
WSDL2.0(原來提議為WSDL1.2,現(xiàn)在重新命名了):第一部分:核心語言(W3C Candidate Recommendation 27 March 2006)
引自:http://www.matrix.org.cn/resource/article/2006-09-24/XFire_6bd1061c-4bc6-11db-978f-43b3336b7e51.html