|
Java遠(yuǎn)程方法調(diào)用(2)
· 傳遞屬性 前面我們講到,RMI可以傳遞屬性,并簡(jiǎn)單介紹了一下一個(gè)有關(guān)開(kāi)支報(bào)告程序的情況。下面我們將深入討論如何設(shè)計(jì)這樣的系統(tǒng)。這樣介紹的目的是使您能夠利用RMI的功能將屬性從一個(gè)系統(tǒng)傳遞到另一個(gè)系統(tǒng),并隨心所欲地安排當(dāng)前的計(jì)算地點(diǎn),并便于將來(lái)的改變。下面的例子并未涉及真實(shí)世界可能發(fā)生的所有問(wèn)題,但可幫助讀者了解處理問(wèn)題的方法。 · 服務(wù)器定義的策略 客戶機(jī)向用戶顯示圖形用戶界面(GUI),用戶填寫開(kāi)支報(bào)告??蛻魴C(jī)程序使用RMI與服務(wù)器進(jìn)行通信。服務(wù)器使用JDBC( Java關(guān)系數(shù)據(jù)庫(kù)連接包)將開(kāi)支報(bào)告存儲(chǔ)在數(shù)據(jù)庫(kù)中。至此,這看起來(lái)與其它多層次系統(tǒng)大同小異,但有一個(gè)重大區(qū)別-- RMI能下載屬性。 假定公司關(guān)于開(kāi)支報(bào)告的政策發(fā)生改變。例如,目前公司只要求對(duì)超過(guò)20美元的開(kāi)支需開(kāi)具發(fā)票。但到明天,公司認(rèn)為這太寬松了,便決定除不超過(guò)20美元的餐費(fèi)以外,任何開(kāi)支均需開(kāi)具發(fā)票。如果不能下載屬性的話,那么在設(shè)計(jì)便于修改的系統(tǒng)時(shí)您可選擇下列方法之一: 用客戶端安裝與政策有關(guān)的程序。政策改變時(shí),必須更新包含此政策的所有客戶端程序。您可在若干服務(wù)器上安裝客戶程序,并要求所有用戶從這些服務(wù)器之一運(yùn)行客戶程序,從而減少問(wèn)題。但這仍不能徹底解決問(wèn)題-- 那些讓程序運(yùn)行好幾天的用戶就無(wú)法使程序更新,而總是會(huì)有一些用戶為了方便而把軟件復(fù)制到本地磁盤上。 您可要求服務(wù)器在每次向開(kāi)支報(bào)告添加項(xiàng)目時(shí)檢查政策。但這樣就會(huì)在客戶機(jī)和服務(wù)器之間產(chǎn)生大量數(shù)據(jù)流,并增加服務(wù)器的工作量。這還會(huì)使系統(tǒng)變得更加脆弱--網(wǎng)絡(luò)故障會(huì)立即妨礙用戶,而不僅僅是只在其呈交開(kāi)支報(bào)告或啟動(dòng)新的報(bào)告時(shí)對(duì)其產(chǎn)生影響。同時(shí),添加項(xiàng)目的速度也會(huì)變慢,因?yàn)檫@需要穿越整個(gè)網(wǎng)絡(luò)往返一圈才能到達(dá)(不堪重負(fù)的)服務(wù)器。 您可在呈交報(bào)告時(shí)要求服務(wù)器對(duì)政策進(jìn)行檢查。這樣就會(huì)使用戶創(chuàng)建很多必須待批報(bào)告的錯(cuò)誤項(xiàng)目,而不是立刻捕捉到第一個(gè)錯(cuò)誤,從而使用戶有機(jī)會(huì)停止制造錯(cuò)誤。為避免浪費(fèi)時(shí)間,用戶需要立刻得到有關(guān)錯(cuò)誤的反饋。 有了RMI,您就能以簡(jiǎn)單的方法調(diào)用程序從服務(wù)器得到屬性,從而提供了一種靈活的方式,將計(jì)算任務(wù)從服務(wù)器卸載到客戶機(jī)上,同時(shí)為用戶提供速度更快的反饋。當(dāng)用戶準(zhǔn)備編寫一份新的開(kāi)支報(bào)告時(shí),客戶機(jī)就會(huì)向服務(wù)器要求一個(gè)對(duì)象,該對(duì)象嵌入了適用于該開(kāi)支報(bào)告的當(dāng)前政策,就如同通過(guò)用Java編寫的政策接口所表示的那樣。該對(duì)象可以以任何方式實(shí)現(xiàn)當(dāng)前政策。如果這是客戶機(jī)RMI首次看到這種專門執(zhí)行的政策,就會(huì)要求服務(wù)器提供一份執(zhí)行過(guò)程的副本。如果執(zhí)行過(guò)程將來(lái)發(fā)生變化,則一種新的政策對(duì)象將被返回給客戶機(jī),而RMI運(yùn)行時(shí)則會(huì)要求得到新的執(zhí)行過(guò)程。 這表明,政策永遠(yuǎn)是動(dòng)態(tài)的。您要想修改政策,就只需簡(jiǎn)單地編寫通用政策接口的新的執(zhí)行程序,把它安裝在服務(wù)器上,并對(duì)服務(wù)器進(jìn)行配置以返回這種新類型的對(duì)象即可。這樣,每臺(tái)客戶機(jī)都會(huì)根據(jù)新的政策對(duì)新的開(kāi)支報(bào)告進(jìn)行檢查。 這是一種比任何靜態(tài)方法都更好的方法,原因如下: 所有客戶機(jī)不必暫停或用新的軟件來(lái)升級(jí)--軟件可根據(jù)需要在不工作時(shí)加以更新。 服務(wù)器不必參與項(xiàng)目檢查工作,該工作可在本地完成。 允許動(dòng)態(tài)限制,因?yàn)閷?duì)象執(zhí)行程序(而不僅僅是數(shù)據(jù))是在客戶機(jī)和服務(wù)器之間進(jìn)行傳遞的。 使用戶能立刻看到錯(cuò)誤。 使客戶機(jī)在服務(wù)器上所能調(diào)用的方法的遠(yuǎn)程接口定義如下: import java.rmi.*; public interface ExpenseServer extends Remote { Policy getPolicy() throws RemoteException; void submitReport(ExpenseReport report) throws RemoteException, InvalidReportException; } import語(yǔ)句輸入Java RMI包。所有RMI類型均在包java.rmi或其子包內(nèi)定義。接口ExpenseServer是一般的Java接口,具有如下兩種有趣的特點(diǎn): 它擴(kuò)展了名為Remote的RMI接口,這使該接口標(biāo)記為可用于遠(yuǎn)程調(diào)用。 它的所有方法均拋出RemoteException,后者用來(lái)表示網(wǎng)絡(luò)或信息故障。 遠(yuǎn)程方法還可拋出您所需要的任何其他例外,但至少必須拋出RemoteException,這樣您才能處理只會(huì)在分布式系統(tǒng)中發(fā)生的錯(cuò)誤狀態(tài)。該接口本身支持兩個(gè)方法:getPolicy (返回一個(gè)實(shí)現(xiàn)政策接口的對(duì)象),和submitReport (提交一個(gè)完成的開(kāi)支請(qǐng)求,并在報(bào)告無(wú)論因何種原因使表格出現(xiàn)錯(cuò)誤時(shí),拋出一個(gè)例外)。 政策接口本身可聲明一種使客戶機(jī)知道能否在開(kāi)支報(bào)告中添加一個(gè)項(xiàng)目的方法: public interface Policy { void checkValid (Expenseentry entry) throws PolicyViolationException; } 如果該項(xiàng)目有效--即符合當(dāng)前政策,則該方法可正常返回。否則就會(huì)拋出一個(gè)描述該錯(cuò)誤的例外。政策接口是本地的(而非遠(yuǎn)程的),所以將通過(guò)本機(jī)對(duì)象在客戶機(jī)上執(zhí)行--在客戶機(jī)的虛擬機(jī)上,而非整個(gè)網(wǎng)絡(luò)上運(yùn)行的對(duì)象。客戶機(jī)可能運(yùn)行下列程序: Policy curPolicy = server.getPolicy(); start a new expense report show the GUI to the user while (user keeps adding entries) { try { curPolicy.checkValid(entry); // throws exception if not OK add the entry to the expense report } catch (PolicyViolationException e) { show the error to the user } } server.submitReport(report); 當(dāng)用戶請(qǐng)求客戶機(jī)軟件啟動(dòng)一份新的開(kāi)支報(bào)告時(shí),客戶機(jī)就調(diào)用server.getPolicy,并要求服務(wù)器返回一個(gè)包含當(dāng)前開(kāi)支政策的對(duì)象。添加的每個(gè)項(xiàng)目首先都被提交給該政策對(duì)象,以獲得批準(zhǔn)。如果政策對(duì)象報(bào)告無(wú)錯(cuò)誤,則該項(xiàng)目就被添加到報(bào)告中;否則錯(cuò)誤就被顯示給用戶,而后者可采取修正措施。當(dāng)用戶完成向報(bào)告中添加項(xiàng)目時(shí),整個(gè)報(bào)告就被呈交。服務(wù)程序如下: import java.rmi. *; import java.rmi.server. *; class ExpenseServerImpl extends UnicastRemoteObject implements ExpenseServer { ExpenseServerImpl() throws RemoteException { // . . . set up server state . . . } public Policy getPolicy() { return new TodaysPolicy(); } public void submitReport(ExpenseReport report) { // . . . write the report into the db . . . } } 除基本程序包外,我們還輸入RMI的服務(wù)程序包。類型UnicastRemoteObject 定義了此服務(wù)程序遠(yuǎn)程對(duì)象的類型,在本例中,應(yīng)為單一服務(wù)程序而非復(fù)制服務(wù)(下面還會(huì)詳細(xì)介紹)。Java類ExpenseSeverImpl實(shí)現(xiàn)遠(yuǎn)程接ExpenseServer的方法。遠(yuǎn)程主機(jī)的客戶機(jī)可使用RMI將信息發(fā)送給ExpenseServerImpl對(duì)象。 |
|
|