摘 要: 介紹了JAXM結(jié)構(gòu),并利用其特點(diǎn)構(gòu)建了不使用消息接發(fā)提供者的Web服務(wù)應(yīng)用,分析了基于JAXM設(shè)計(jì)的體系結(jié)構(gòu)和設(shè)計(jì)模式。
關(guān)鍵詞: JAXM規(guī)范 SAAJ包 SOAP規(guī)范 Web服務(wù)
JAXM(Java API for XML Messaging)是基于SOAP1.1規(guī)范并帶有Attachments的SOAP規(guī)范,它定義了一個(gè)能夠與XML消息交換的基本框架。JAXM提供了一種能夠在Java平臺上通過Internet發(fā)送XML文檔的標(biāo)準(zhǔn)方法,使得開發(fā)者可以使用Java平臺進(jìn)行XML消息收發(fā)。而大多數(shù)的Web服務(wù)都是由簡單的消息交換組成,對于有些Web服務(wù),直接在低級別的消息層編程通常是很有用的。JAXM提供了使XML數(shù)據(jù)能夠在二個(gè)應(yīng)用之間互操作的框架,允許在二個(gè)獨(dú)立的Web服務(wù)之間完成全部XML文件的傳輸,而SOAP提供了在二個(gè)應(yīng)用之間傳輸消息的底層格式。
1 JAXM相關(guān)概念
完整的JAXM API存在于javax.xml.soap和javax.xml.messaging二個(gè)包中。javax.xml.soap是用于接發(fā)SOAP消息的基本包,主要包含了發(fā)送帶有附件的SOAP消息的API(SOAP with Attachments API for Java,SAAJ)。這個(gè)包包含所有發(fā)送請求-響應(yīng)消息所必需的API。javax.xml.messaging包含了使用消息接發(fā)提供者必須的API,因此能夠發(fā)送單向消息?,F(xiàn)在SAAJ不再依賴于javax.xml.messaging包,可以獨(dú)立使用。在JAXM1.1規(guī)范中定義的javax.xml.messaging包保持了對java.xml.soap包的依賴。
JAXM消息遵循SOAP標(biāo)準(zhǔn),規(guī)定了消息的格式。通過JAXM API可以方便地創(chuàng)建遵從SOAP規(guī)范的XML消息。有二類SOAP消息,一類是帶有附件的消息,另一類是沒有附件的消息。SOAP消息都是通過連接進(jìn)行接發(fā)的,有二種類型的連接,分別由JAXM API提供的類和接口來表示。javax.xml.soap.SOAPConnection表示了消息發(fā)送者到接收者的直接連接,這種類型的連接是點(diǎn)對點(diǎn)的,即使不在Servlet或者J2EE容器里也能使用。這種類型的消息接發(fā)又稱為請求-響應(yīng)消息接發(fā)。javax.xml.messaging.ProviderConnection表示了到消息接發(fā)提供者的連接,這種連接方式需要消息發(fā)送者和消息使用者通過消息提供者來交互。
2 使用JAXM建立Web服務(wù)消息接發(fā)
JAXM客戶端可以使用消息接發(fā)提供者,也可以不用。沒有使用消息接發(fā)提供者的應(yīng)用程序被限制只能承擔(dān)客戶端的角色,只能發(fā)送請求-響應(yīng)消息。下面介紹建立不使用消息接發(fā)提供者的Web服務(wù)消息接發(fā),即點(diǎn)對點(diǎn)的請求-響應(yīng)消息接發(fā)。
(1)獲得連接。任何一個(gè)JAXM客戶端需要完成的第一件事是獲得連接。當(dāng)前客戶端使用SOAPConnection對象創(chuàng)建連接,使用SoapConnection對象發(fā)送的消息直接從發(fā)送者到發(fā)送者指定的URL,示例如下:
SOAPConnectionFactory scf=SOAPConnectionFactory.
newInstance( );
SOAPConnection con=scf.createConnection( );
(2)創(chuàng)建消息。生成消息可以使用SAAJ API提供的MessageFactory類的默認(rèn)實(shí)現(xiàn)。
MessageFactory mf=MessageFactory.newInstance( );
SOAPMessage msg=mf.createMessage( );
由messageFactory創(chuàng)建的所有SOAPMessage對象都是基本SOAP消息,這意味著它們沒有預(yù)定義的頭部。JAXM所具有的靈活性之一就是它允許SOAP頭部有特殊的用途。例如,像ebXML這樣的一些協(xié)議可以建立在SOAP消息發(fā)送協(xié)議之上,以便提供附加頭部的實(shí)現(xiàn),這樣就獲得了附加的功能。
(3)向消息添加內(nèi)容??梢韵騍OAPPart對象以及一個(gè)或多個(gè)AttachmentPart對象添加內(nèi)容,也可以向消息的這二個(gè)部分都添加內(nèi)容。為了向消息體添加內(nèi)容,需創(chuàng)建一個(gè)SOAPBodyElement對象并添加一個(gè)使用SOAPElement.addTextNode方法建立的XML元素。
(4)發(fā)送消息。一旦填充了一個(gè)SOAPMessage對象,就可以發(fā)送此對象了。單獨(dú)客戶端使用SOAPConnection的call方法來發(fā)送消息。該方法發(fā)送完消息之后就將自己阻塞起來,直到收到響應(yīng)為止。傳給call方法的參數(shù)中,一個(gè)是要發(fā)送的消息,還有一個(gè)是URL對象,該對象包含了接收者端點(diǎn)的URL。
(5)從響應(yīng)消息中檢索內(nèi)容。客戶端使用onMessage方法檢索消息內(nèi)容。客戶端通過消息得到envelope,再通過envelope得到body,從而訪問SOAPBody對象。
3 使用JAXM構(gòu)建Web服務(wù)應(yīng)用
下面使用JAXM 構(gòu)建對外提供話費(fèi)查詢的Web服務(wù)。由于客戶端是Web服務(wù)的消費(fèi)者,所以JAXM客戶端不需要消息接發(fā)提供者,使用點(diǎn)對點(diǎn)的請求-響應(yīng)消息接發(fā)即可。話費(fèi)查詢服務(wù)系統(tǒng)體系結(jié)構(gòu)如圖1所示。
客戶端可以是一般的Java GUI程序(當(dāng)然也可以是JSP、Servlet等),客戶端通過SOAP消息與Servlet容器中運(yùn)行的JAXM Servlet進(jìn)行交互。JAXM Servlet是服務(wù)提供者,EJB容器里運(yùn)行的是業(yè)務(wù)組件,為JAXM Servlet提供服務(wù)。

系統(tǒng)為客戶端提供了三種查詢服務(wù):每月匯總查詢、話費(fèi)詳單查詢、即時(shí)詳單查詢。這三種服務(wù)分別對應(yīng)服務(wù)端的三個(gè)JAXM Servlet實(shí)現(xiàn):TotalMonth,每月匯總查詢;DetailMonth,話費(fèi)詳單查詢;PrestentDetail,即時(shí)詳單查詢。
(1)數(shù)據(jù)模型。在數(shù)據(jù)庫中保存每個(gè)用戶的話費(fèi)信息。為了便于傳輸數(shù)據(jù),減少遠(yuǎn)程調(diào)用的次數(shù),特別設(shè)計(jì)了值對象TelExpVA來代表話費(fèi)信息。值對象設(shè)計(jì)模式在J2EE模式中大量使用。JAXM Servlet和EJB組件之間傳遞數(shù)據(jù)就是通過對象來傳遞的。這個(gè)對象是包含有TelExpVA實(shí)例的java.util.Collection。但是JAXM和客戶端是通過SOAP消息來傳遞的(也可以使用序列化的對象作為附件發(fā)送)。為了傳輸話費(fèi)信息,還需要定義相對應(yīng)的DTD或者schema。
(2)業(yè)務(wù)邏輯。業(yè)務(wù)層是EJB組件,使用了二個(gè)EJB組件,一個(gè)是TelExpServiceFacadeEJB,它是一個(gè)有狀態(tài)會話Bean;另一個(gè)是TelExpEntityEJB,它是一個(gè)實(shí)體Bean,代表了持久數(shù)據(jù)。TelExpServiceFacadeEJB是會話門面,JAXM Servlet通過它來和TelExpEntityEJB交互。
(3)JAXM服務(wù)端。JAXM服務(wù)端Servlet處理消息的步驟是:獲得消息(onMessage),讀取消息中需要的參數(shù),利用參數(shù)調(diào)用對應(yīng)的業(yè)務(wù)處理,構(gòu)建響應(yīng)SOAP消息,返回處理后的消息。
在JAXM 服務(wù)端設(shè)計(jì)了三個(gè)服務(wù)JAXM Servlet,分別對應(yīng)三個(gè)查詢用例,其關(guān)系如圖2所示。

由于使用了點(diǎn)對點(diǎn)的消息模型,服務(wù)端需要實(shí)現(xiàn)javax.xml.messaging.ReqRespListener接口,并且需要繼承javax.xml.messaging.JAXMServlet類。onMessage方法就是當(dāng)Servlet接收到SOAPMessage時(shí)激發(fā)的方法,通過此方法對外界提供服務(wù)。每個(gè)JAXM Servlet都有一個(gè)onMessage方法。
(4)客戶端。最終客戶端是一個(gè)叫TelExpClientGUI的圖形界面程序,它并不直接與SOAP消息打交道,而是通過JAXMDelegate類與服務(wù)端JAXM Servlet進(jìn)行交互。另外,還設(shè)計(jì)了一個(gè)BookBusiness接口。JAXMDelegate實(shí)現(xiàn)了BookBusiness接口,BookClientGUI持有BookBusiness的實(shí)例,它通過這個(gè)實(shí)例獲得信息。BookBusiness返回的信息都是java.util.Collection,它是BookClientGUI和JAXMDelegate通信的橋梁。
(5)使用XML業(yè)務(wù)代表的模式。用JAXM進(jìn)行Web服務(wù)開發(fā)還不普遍,相應(yīng)的設(shè)計(jì)模式的探討還較少,它的設(shè)計(jì)模式和J2EE平臺其他組件的設(shè)計(jì)模式基本一致。但它有自己的特點(diǎn),如客戶端和服務(wù)端是通過SOAP消息進(jìn)行通信,這與J2EE平臺的其他組件之間的通信不同。在JAXM編程中,為了實(shí)現(xiàn)數(shù)據(jù)(這里是SOAP消息)的一致返回,可以使用XML業(yè)務(wù)代表的模式。用JAXM進(jìn)行編程時(shí),數(shù)據(jù)傳遞的特點(diǎn)如圖3所示。

客戶端最終要使用的數(shù)據(jù)是Java對象或者Java的基本數(shù)據(jù)類型,而客戶端和服務(wù)端的通信是通過SOAP消息格式來傳輸,而在服務(wù)端要調(diào)用業(yè)務(wù)邏輯,必須使用Java對象或者是Java基本數(shù)據(jù)類型,這使數(shù)據(jù)的傳輸和數(shù)據(jù)的使用出現(xiàn)矛盾。為解決矛盾,可以使用數(shù)據(jù)轉(zhuǎn)換器來轉(zhuǎn)換數(shù)據(jù),以降低層之間的耦合度,使數(shù)據(jù)易于處理。當(dāng)客戶端要發(fā)送數(shù)據(jù)時(shí),使用數(shù)據(jù)轉(zhuǎn)換器把請求數(shù)據(jù)轉(zhuǎn)換為SOAP消息格式。當(dāng)在服務(wù)端調(diào)用業(yè)務(wù)邏輯后,為使數(shù)據(jù)能在internet上傳輸,使用數(shù)據(jù)轉(zhuǎn)換器把調(diào)用結(jié)果封裝為SOAP消息。
在客戶端,通過使用JAXM業(yè)務(wù)代表,可以降低最終客戶和SOAP消息的耦合度。系統(tǒng)的結(jié)構(gòu)如圖4所示。

當(dāng)客戶端發(fā)出請求時(shí),它調(diào)用JAXMDelegate對應(yīng)的方法;JAXMDelegate根據(jù)請求構(gòu)造對應(yīng)的SOAP消息,然后把消息發(fā)送到服務(wù)端;服務(wù)端根據(jù)客戶的請求做出對應(yīng)的處理,并把處理結(jié)果返回到JAXMDelegate;JAXMDelegate使用數(shù)據(jù)轉(zhuǎn)換器把返回的SOAP Message轉(zhuǎn)化成Java對象(如值Bean),最后返回給客戶端;客戶端再把獲得的數(shù)據(jù)進(jìn)行處理后顯示。JAXM業(yè)務(wù)代表使用數(shù)據(jù)轉(zhuǎn)換器來轉(zhuǎn)換數(shù)據(jù),業(yè)務(wù)代表直接和Web服務(wù)進(jìn)行交互,屏蔽了Web服務(wù)請求的復(fù)雜過程,為客戶端提供易于使用的接口。
服務(wù)端的模式和客戶端的模式基本一樣,只是處理過程相反。服務(wù)端從客戶端接收到SOAP消息后,讀取參數(shù),調(diào)用對應(yīng)的業(yè)務(wù)方法,然后使用數(shù)據(jù)轉(zhuǎn)換器把調(diào)用的結(jié)果轉(zhuǎn)換成SOAP消息返回。
4 結(jié)束語
JAXM作為輕量級的API,抽象了底層的消息基礎(chǔ)結(jié)構(gòu)。因此,開發(fā)時(shí)利用SOAP包裝的JAXM消息是容易的。需要強(qiáng)調(diào)的是,如果使用點(diǎn)對點(diǎn)的消息發(fā)送模型,則服務(wù)端Servlet必須實(shí)現(xiàn)ReqRespListener接口,onMessage( )方法將是開發(fā)服務(wù)端Servlet的重點(diǎn)任務(wù)。本文通過使用適當(dāng)?shù)脑O(shè)計(jì)模式和接口,減少了各層之間的耦合。數(shù)據(jù)轉(zhuǎn)換在設(shè)計(jì)中占有很大的分量,業(yè)務(wù)代表模式在以上數(shù)據(jù)轉(zhuǎn)換和業(yè)務(wù)處理中起著重要的作用。
參考文獻(xiàn)
1 Sun Microsystems Inc.The Java Web Services Tutorial. http://java.sun.com/webservices/docs/1.0/tutorial/index.html.
2 Kao J.Developer′s Guide to Building XML-based Web Services with the Java 2 Platform,Enterprise Edition (J2EE). http://www.middleware-company.com.
