Java遠程方法調用 4

與現有服務器的連接

  人們常說,RMI主要是“從Java到Java”,但這種說法掩蓋了這樣一個事實:Java可使用被稱爲JNI的本機方法接口,很容易地與現有和原有系統連接。JNI和RMI的混合使用與任何其它Java程序一樣簡單。您可使用JDBC,再結合RMI,與現有的關係數據庫連接。也就是說,您可使用RMI連接二層次和三層次系統--即使雙方都不是用Java 編寫的亦可。這樣做有很大的好處和優勢,下面會詳細闡述。但首先讓我們看一看它是如何完成的。

假定您現在有一臺在關係數據庫中存儲了有關客戶訂單信息的服務器。在任何多層次系統中,您都得設計一個遠程接口,以便於客戶機訪問服務器--利用作爲Remote 接口的RMI:

import java.rmi.*;
import java.sql.SQLException;
import java.util.Vector;
public interface OrderServer extends Remote {
Vector getUnpaid() throws RemoteException, SQLException;
void shutDown() throws RemoteException;
// ... other methods (getOrderNumber, getShipped, ...)
}
java.sql包包含JDBC包。每個遠程方法均可被服務器採用實際數據庫的JDBC調用,或通過採用其它數據庫訪問機制的本機方法實現。上面所示的方法返回一個Order (訂單) 對象的Vector (列表)。Order (訂單)就是在您的系統中定義的、用來保存客戶訂單的類。
本節將介紹如何使用JDBC實現getUnpaid,和如何使用JNI 實施shutDown。

JDBC -- 連接數據庫

  使用JDBC實現getUnpaid的OrderServerImpl如下:

import java.rmi.*;
import java.rmi.server.*;
import java.sql.*;
import java.util.Vector;
public class OrderServerImpl
extends UnicastRemoteObject
implements OrderServer
{
Connection db; // connection to the db
PreparedStatement unpaidQuery; // unpaid order query
OrderServerImpl() throws RemoteException, SQLException {
db = DriverManager.getConnection("jdbc:odbc:orders");
unpaidQuery = db.preparedStatement("…");
}
public Vector getUnpaid() throws SQLException {
ResultSet results = unpaidQuery.executeQuery();
Vector list = new Vector();
while (results.next())
list.addElement(new Order(results));
return list;
}
public native void shutDown();
}
其中大多是JDBC任務。除了以Order開始的類型是您的系統的一部分類型以外,您所看到的所有類型均爲JDBC或 RMI的一部分。構造函數初始化OrderServerImpl對象,創建與jdbc URL中所規定的數據庫的連接( Connection)。有了這個連接,我們就可以使用prepareStatement定義一個能找到所有未付款訂單的查詢。在此還可爲其它方法定義其它查詢。OrderServerImpl作爲數據庫在同一個系統上運行,而且還可能是在同一個進程中(下面將討論shutDown)。
當getUnpaid方法在RMI服務器對象OrderServerImpl上被調用後,就會執行預先編譯的查詢,並返回包含所有匹配元素的JDBC ResultSet對象。隨後,我們爲結果集中的每個項目創建新的Order對象,並將其添加到Vector對象中( Java 的動態數組)。在結束讀取結果後,我們將這個向量返回給客戶機,後者可將結果顯示給用戶,或者其他相關的人。

JNI -- 本機方法

  RMI服務器和客戶機可利用本機方法與現有的和原有的系統連接。您可使用本機方法實現不能直接訪問數據庫的遠程方法,或者通過採用現有代碼更簡單地實現。您可使用本機接口JNI編寫C和C++程序,以實現?Java方法並 Java對象上調用該方法。用本機方法實現shutDown的程序如下:

JNIEXPORT void JNICALL
Java_OrderServerImpl_shutDown(JNIEnv *env, jobject this)
{
jclass cls;
jfieldID fid;
DataSet *ds;
cls = (*env)->GetObjectClass(env, this);
fid = (*env)->GetFieldID(env, cls, "dataSet", "J");
ds = (DataSet *) (*env)->GetObjectField(env, this, fid);
/* With a DataSet pointer we can use the original API */
DSshutDown(ds);
}
這是假定了現有服務器通過其API定義的DataSet類型得到了引用。指向服務器DataSet的指針存儲在dataSet域中。當客戶機調用shutDown時,服務器的shutDown方法就會被調用。因爲在服務器實現中聲明瞭要用本機方法來實現shutDown 方法,所以,RMI將直接調用這個本機方法。這個本機方法找到對象的dataSet域,得到其值,並用它調用現有API 的函數DSshutDown。
  Sun公司目前正與ILOG公司合作,開發一種稱作TwinPeaks 的產品。TwinPeaks將能夠兼容目前的C和C++ API,並生成 Java類,該Java類包含了到Java類中API的調用。這樣,您就能從Java調用現有的任何API。TwinPeaks面市後,將有可能完全使用Java (而非JNI調用)編寫諸如shutDown這樣的方法。

體系結構

  RMI系統可爲分佈式面向對象計算提供簡單而又直接的基礎。其體系結構可允許對服務器和引用類型進行擴展,從而使RMI能以連續的方式添加功能。

  當服務程序被輸出後,其引用類型就被定義。在上面的例子中,我們將服務器作爲UnicastRemoteObject服務器輸出,即點到點非複製服務器。對這些對象的引用對於這類服務器非常合適。不同類型的服務器有不同的引用句法。例如,MulticastRemoteObject就有允許複製服務的引用句法。

rmi_white_paper_image2.gif


  當客戶機收到向服務器的引用後,RMI就會下載一個可將該引用上的調用轉換爲面向服務器的遠程調用的存根。如圖3所示,存根使用對象序列化法將參數編組到方法中,並通過網絡將經過編組的調用送到服務器。在服務器端,RMI系統接收調用,並連接到一個框架上,而框架則負責解除參數編組並在服務器上調用該方法的實現。當服務器的執行完成後,無論返回一個值或拋出一個例外,框架通過對結果進行編組,並向客戶機的存根發送一個應答。存根解除應答編組,並根據需要返回一個值或拋出一個例外。存根和框架是用服務器的實現生成的,通常使用的是程序rmic。存根使用引用與框架進行會話。這種體系結構使引用能夠定義通信的屬性。用於 UnicastRemoteObject服務器的引用與在特定主機和端口上運行的單個服務器對象進行通信。憑藉存根/引用的分離功能,RMI就能添加新的引用類型。處理複製服務器的引用可將服務器請求多路發送給一組正確的複製程序,彙集響應,再根據多種響應返回正確的結果。如果服務程序沒有在虛擬機上運行,則另一個引用類型可激活該程序。客戶機可透明地與所有這些引用類型共同工作。

保密與安全

  在執行RMI請求時,安全是絕對有保證的。RMI可在客戶機與服務器之間提供安全信道,並可將下載的執行程序放入安全的“沙箱(sandbox)”中運行,從而保護您的系統免遭不明客戶機可能的攻擊。

  首先,必須定義您的安全需求,這非常重要。如果您正在安全的企業網內部執行諸如ComputeServer這樣的程序,則您只需知道誰在使用計算環路即可,這樣您就能對濫用系統的人進行跟蹤。如果您需要提供商業計算服務器,則您就需要防止多種惡意的破壞。這些都會影響接口的設計--在企業內部,您可能只要求每個Task對象都配備人名和部門編號,以便跟蹤。而在商業領域中,您可能需要更高的安全性,包括數字簽名身份識別和某些能幫助您剔除會耗費超過分配於其時間的惡意任務的合同語言。

  在客戶機和服務器之間您可能需要有一個安全信道。 RMI可使您能提供可創建包括加密插口(socket)在內的您所需要的任何類型插口的插口工廠。從JDK 1.2開始,您將能夠指定服務器插口所提供的服務的要求(通過給出對這些要求的描述)。這種新技術可在小應用程序上採用,而多數瀏覽器都拒絕承諾設置插口工廠。插口要求可包括加密和其它要求。

  下載的類也存在安全問題。Java通過SecurityManager對象處理安全問題,而該對象可傳遞所有與安全有關操作的判斷,如打開文件和網絡連接等。RMI通過要求您在輸出任何服務對象或在服務器上調用任何方法之前安裝安全管理器,以使用這個標準的Java機制。RMI提供與小應用程序(無文件存取,只是連接到發出主機等)限制相同的 RMISecurityManager類型。這樣可防止下載的執行程序從計算機上讀取或寫入數據、或與防火牆後面的其它系統連接。您也可編寫和安裝自己的安全管理程序對象,以執行不同的安全限制。

防火牆

  RMI爲防火牆後面的客戶機提供了與遠程服務器進行通信的方法。這樣可使您使用RMI在因特網上部署客戶程序,如在用於萬維網上的小應用程序中。穿越客戶機的防火牆會使通信速度降低,所以RMI成功地採用速度最快的技術連接客戶機與服務器。當客戶機首次依次嘗試下列三種可能的方法,試圖與服務器建立通信時,該技術即被UnicastRemoteObject的引用所發現

用插口(socket)直接與服務器的端口通信。


如果失敗,則建立連接到服務器主機和端口上的URL,並在該URL上使用HTTP POST請求,將信息作爲POST的主體發送給框架。如果成功,則post的結果就是框架對存根的響應。


如果又失敗,則用端口80 (標準HTTP端口)建立連接到服務器主機的URL,並使用能夠將?MI請求發送給服務器的 CGI程序。


這三種技術哪一種首先成功,將來就會首先被用來與服務器進行通信。如果這些技術都不成功,則遠程方法調用失敗。


這種三步策略使客戶機能夠以儘可能高的效率實現通信,並大多使用直接插口連接。在沒有安裝防火牆的系統上,或在企業內部防火牆後的通信中,客戶機將使用插口直接連接到服務器上。二級通信技術的速度比直接通信明顯地要慢得多,但允許您編寫能在因特網和萬維網上廣泛使用的客戶程序。
發佈了117 篇原創文章 · 獲贊 1 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章