熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java遠程方法調用(2)

2013-11-23 19:18:42  來源: Java核心技術 

  傳遞屬性
  
  
    前面我們講到RMI可以傳遞屬性並簡單介紹了一下一個有關開支報告程序的情況下面我們將深入討論如何設計這樣的系統這樣介紹的目的是使您能夠利用RMI的功能將屬性從一個系統傳遞到另一個系統並隨心所欲地安排當前的計算地點並便於將來的改變下面的例子並未涉及真實世界可能發生的所有問題但可幫助讀者了解處理問題的方法
  
  服務器定義的策略
  
    圖是可進行動態配置的開支報告系統的示意圖客戶機向用戶顯示圖形用戶界面(GUI)用戶填寫開支報告客戶機程序使用RMI與服務器進行通信服務器使用JDBC( Java關系數據庫連接包)將開支報告存儲在數據庫中至此這看起來與其它多層次系統大同小異但有一個重大區別 RMI能下載屬性
  假定公司關於開支報告的政策發生改變例如目前公司只要求對超過美元的開支需開具發票但到明天公司認為這太寬松了便決定除不超過美元的餐費以外任何開支均需開具發票如果不能下載屬性的話那麼在設計便於修改的系統時您可選擇下列方法之一
  
    用客戶端安裝與政策有關的程序政策改變時必須更新包含此政策的所有客戶端程序您可在若干服務器上安裝客戶程序並要求所有用戶從這些服務器之一運行客戶程序從而減少問題但這仍不能徹底解決問題 那些讓程序運行好幾天的用戶就無法使程序更新而總是會有一些用戶為了方便而把軟件復制到本地磁盤上
    您可要求服務器在每次向開支報告添加項目時檢查政策但這樣就會在客戶機和服務器之間產生大量數據流並增加服務器的工作量這還會使系統變得更加脆弱網絡故障會立即妨礙用戶而不僅僅是只在其呈交開支報告或啟動新的報告時對其產生影響同時添加項目的速度也會變慢因為這需要穿越整個網絡往返一圈才能到達(不堪重負的)服務器
  您可在呈交報告時要求服務器對政策進行檢查這樣就會使用戶創建很多必須待批報告的錯誤項目而不是立刻捕捉到第一個錯誤從而使用戶有機會停止制造錯誤為避免浪費時間用戶需要立刻得到有關錯誤的反饋
    有了RMI您就能以簡單的方法調用程序從服務器得到屬性從而提供了一種靈活的方式將計算任務從服務器卸載到客戶機上同時為用戶提供速度更快的反饋當用戶准備編寫一份新的開支報告時客戶機就會向服務器要求一個對象該對象嵌入了適用於該開支報告的當前政策就如同通過用Java編寫的政策接口所表示的那樣該對象可以以任何方式實現當前政策如果這是客戶機RMI首次看到這種專門執行的政策就會要求服務器提供一份執行過程的副本如果執行過程將來發生變化則一種新的政策對象將被返回給客戶機而RMI運行時則會要求得到新的執行過程
    這表明政策永遠是動態的您要想修改政策就只需簡單地編寫通用政策接口的新的執行程序把它安裝在服務器上並對服務器進行配置以返回這種新類型的對象即可這樣每台客戶機都會根據新的政策對新的開支報告進行檢查
  
  這是一種比任何靜態方法都更好的方法原因如下
  
    所有客戶機不必暫停或用新的軟件來升級軟件可根據需要在不工作時加以更新
    服務器不必參與項目檢查工作該工作可在本地完成
    允許動態限制因為對象執行程序(而不僅僅是數據)是在客戶機和服務器之間進行傳遞的
    使用戶能立刻看到錯誤
  
  
  使客戶機在服務器上所能調用的方法的遠程接口定義如下
  import javarmi*;
  public interface ExpenseServer extends Remote {
  Policy getPolicy() throws RemoteException;
  void submitReport(ExpenseReport report)
  throws RemoteException InvalidReportException;
  }
  import語句輸入Java RMI包所有RMI類型均在包javarmi或其子包內定義接口ExpenseServer是一般的Java接口具有如下兩種有趣的特點
  
  
  它擴展了名為Remote的RMI接口這使該接口標記為可用於遠程調用
  
  
  它的所有方法均拋出RemoteException後者用來表示網絡或信息故障
  
    遠程方法還可拋出您所需要的任何其他例外但至少必須拋出RemoteException這樣您才能處理只會在分布式系統中發生的錯誤狀態該接口本身支持兩個方法getPolicy (返回一個實現政策接口的對象)和submitReport (提交一個完成的開支請求並在報告無論因何種原因使表格出現錯誤時拋出一個例外)
  政策接口本身可聲明一種使客戶機知道能否在開支報告中添加一個項目的方法
  public interface Policy {
  void checkValid (Expenseentry entry)
  throws PolicyViolationException;
  }
  如果該項目有效即符合當前政策則該方法可正常返回否則就會拋出一個描述該錯誤的例外政策接口是本地的(而非遠程的)所以將通過本機對象在客戶機上執行在客戶機的虛擬機上而非整個網絡上運行的對象客戶機可能運行下列程序
  Policy curPolicy = servergetPolicy();
  start a new expense report
  show the GUI to the user
  while (user keeps adding entries) {
  try {
  curPolicycheckValid(entry); // throws exception if not OK
  add the entry to the expense report
  } catch (PolicyViolationException e) {
  show the error to the user
  }
  }
  serversubmitReport(report);
  
  
    當用戶請求客戶機軟件啟動一份新的開支報告時客戶機就調用servergetPolicy並要求服務器返回一個包含當前開支政策的對象添加的每個項目首先都被提交給該政策對象以獲得批准如果政策對象報告無錯誤則該項目就被添加到報告中否則錯誤就被顯示給用戶而後者可采取修正措施當用戶完成向報告中添加項目時整個報告就被呈交服務程序如下
  import javarmi *;
  import javarmiserver *;
  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的服務程序包類型UnicastRemoteObject 定義了此服務程序遠程對象的類型在本例中應為單一服務程序而非復制服務(下面還會詳細介紹)Java類ExpenseSeverImpl實現遠程接ExpenseServer的方法遠程主機的客戶機可使用RMI將信息發送給ExpenseServerImpl對象
  
  
  本文中討論的重要方法是getPolicy它簡單地返回定義當前政策的對象下面看一個執行政策的例子
  
  public class TodaysPolicy implements Policy {
  public void checkValid(ExpenseEntry entry)
  throws PolicyViolationException
  {
  if (entrydollars() < 20) {
  return; // no receipt required
  else if (entry.haveReceipt() == false) {
  throw new PolicyViolationException;
  }
  }
  }
  TodaysPolicy進行檢查的目的是確保無收據的任何項目均少於20美元。tW.WingWit.COM如果將來政策發生變化,僅少於20美元的餐費可不受“需要收據”政策的限制,則您可提供新的政策實現:
  public class TomorrowsPolicy implements Policy {
  public void checkValid(ExpenseEntry entry)
  throws PolicyViolationException
  {
  if (entry.isMeal() && entry.dollars() < 20) {
  return; // no receipt required
  } else if (entry.haveReceipt() == false) {
  throw new PolicyViolationException;
  }
  }
  }
    編寫這個類,並把它安裝在服務器上,然後告訴服務器開始提供TomorrowsPolicy對象,而非daysPolicy對象,這樣您的整個系統就會開始使用新的政策。當客戶機調用服務器的getPolicy方法時,客戶機的RMI就會檢查返回的對象是否為已知類型。每台客戶機首次遇到TomorrowsPolicy時,RMI就會在getPolicy返回前下載政策的實現。客戶機可輕松地開始增強這個新的政策。
  
  
    RMI使用標准Java對象序列化機制傳遞對象。引用遠程對象參數作為遠程引用傳遞。如果向某方法提供的參數為原始類型或本機(非遠程)對象,則向服務器傳遞一個深副本。返回值也拾?照同樣的方式處理,只不過是沿其它方向。RMI可使用戶向本機對象傳遞和返回完整對象圖並為遠程對象傳遞和返回引用。
  
    在真實的系統中,getPolicy方法可能會有一個可以識別用戶及開支報告類型(差旅、客戶關系等)的參數,這樣可使政策加以區別。您或者可以不要求單獨的政策和開支報告對象,但您可以有一種newExpenseReport方法,它可返回一個直接檢查政策的ExpenseReport對象。這最後一種策略可使您像修改政策一樣簡單地修改開支報告的內容--當公司決定需要把餐費劃分為早餐、午餐和晚餐項目,而且像上述修改政策一樣簡單地執行修改時--可編寫一個實現該報告的新類,客戶程序就會自動使用這個類。

From:http://tw.wingwit.com/Article/program/Java/hx/201311/26634.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.