6 月份,我們談過您爲什麼要使用 CORBA 和 Java 技術。本月,我要通過一個可用的簡單示例,讓您開始探索 CORBA 技術的許多領域。不過,別忘了我們的目標是,創建這樣一種分佈式應用程序:使駐留在一臺計算機上的客戶機能向運行於另一臺計算機上的服務發出請求。我們不想爲諸如硬件或操作系統軟件等細節問題操心,而只是想讓這種服務能響應客戶機的請求。
|
這個接口中的 IDL 關鍵字有:module、interface、long 和 in。IDL 使用關鍵字 module
來創建名稱空間,並且此關鍵字準確地映射爲 Java 關鍵字 package
。運行 IDL-to-Java 編譯器時,生成的 Java 文件將會存到名爲 calcsimpl
的子目錄中。IDL 關鍵字 interface
完美地映射爲 Java 接口,並代表一種抽象類型,因爲兩者都只定義您與對象通訊的方式,而不涉及對象的實現。IDL 關鍵字 long
是一種基本的整數類型,它至少映射爲一個 4 字節的類型,這種類型在 Java 代碼中就是 int。
想一想執行遠程方法調用的機制,您就會發現定義參數傳遞的方向(客戶機到服務器、服務器到客戶機或者雙向傳遞)是多麼的有意義。在 IDL 操作中,這些方向用 in
、out
和 inout
關鍵字來聲明,每個參數都必須聲明方向,以便使對象請求代理程序 (ORB) 知道該參數的去向。這會影響到爲發送而進行的參數打包、參數解包以及內存管理。ORB 對參數瞭解得越多,它的效率就越高。關鍵字 in
表明 long x
和 long y
是從客戶機傳遞到服務器。
需要 IDL 編譯器嗎? |
接口定義以後,必須在 ORB 供應商提供的 IDL-to-Java 編譯器上運行。IDL 編譯器是一種精巧的實用程序,它生成 IDL 的 stub 和 skeleton 以及其它支持文件。生成的這些源文件,大部分將增強 CORBA 標準中定義的特定 IDL 類型的打包功能。編譯器將生成大部分網絡探測 (plumbing),這在分佈式系統中非常重要。在最基本的級別中,IDL-to-Java 編譯器只是一個按 CORBA 2.3 規範的定義來實現從 IDL 到 Java 語言映射的程序。手動生成這些代碼既枯燥又費時,還容易出錯;IDL-to-Java 編譯器會處理這一切,所以您就不用操心啦;同時,它會用一定的規則約束您,並強制您執行封裝。IDL-to-Java 編譯器將把 CORBA-land 規則強加給您的系統。
輸入下面的命令,從 Orbacus 執行 IDL-to-Java 編譯器,把所有生成的文件都放在 CLASSPATH 的輸出目錄下。
清單 2. 調用 IDL-to-Java 編譯器
|
生成了什麼呢?這個命令生成了構建實現所需要的全部 Java 源文件。IDL-to-Java 編譯器可確保所定義的接口遵守 CORBA 規範的規則。
下面是這些文件:
- calculator.java - 這個文件叫標記接口文件。CORBA 規範指出這個文件必須擴展 IDLEntity,並且與 IDL 接口同名。這個文件提供類型標記,從而使這個接口能用於其它接口的方法聲明。
- calculatorOperations.java - 這個文件內含 Java 公共接口 -- calculatorOperations。規範指出,這個文件應該與具有
Operations
後綴的 IDL 接口同名,並且這個文件內含此接口映射的操作標記。上面定義的標記接口 (calculator.java
) 可擴展這個接口。 - calculatorHelper.java - 設計 helper 類的目的是,讓所需要的許多內務處理功能脫離我們的接口,但又隨時可用到實現過程中。幫助程序文件含有重要的靜態 narrow 方法,這種方法使
org.omg.CORBA.Object
收縮爲一種更具體的類型的對象引用;在這種情況下,將是一個計算程序類型。 - calculatorHolder.java - holder 類是一個專門化類,是爲了需要通過引用來傳遞參數的任意數據類型而生成的。這個示例中將不使用 holder 類,但我們將會在以後的欄目中經常見到它。
- calculatorPOA.java - skeleton 類爲 CORBA 功能提供了請求-響應探測的一大部分。生成
calculatorPOA.java
,是因爲缺省方式下的實現是基於繼承的,如果我們選擇基於委託的實現方式,輸出就會不一樣。這些主題將在以後的欄目中詳細介紹。 - _calculatorStub.java - 顧名思義,這是一個 stub 類。您的客戶機將需要這個類來進行工作。
服務器
現在生成的文件必須在服務器上開始工作,用這個服務器實現我們的接口。所幸的是,大部分探測是適合我們的要求的,但別高興得太早 -- 還有許多工作要做;就是說,所有這些文件都必須用在正確的地方。
讓我們從 add()
方法的實現開始。(您可以下載完整的 文件。)
清單 3. SimpleCalcSvr.java -- add()
方法
|
請注意,我們的實現類擴展了已生成的類 calculatorPOA
。從客戶機發來一個請求時,該請求通過 ORB 進入 skeleton,skeleton 最終將調用 SimpleCalcServant
,來完成請求並啓動響應。我們的接口很簡單,所以我們的實現也很簡單。
服務器其餘部分的實現,涉及如何圍繞這個接口實現來設置 CORBA 體系結構,由於可移植性和靈活性方面的原因,許多這些調用要按 CORBA 規範執行。
我們需要完成的第一項任務是,詳細說明要使用哪一個 ORB,然後予以初始化。下面的代碼(文件 SimpleCalcSvr.java 的第 18 行到第 29 行)處理此任務:
清單 4. SimpleCalcSvr.java -- 初始化 ORB
|
初始化 ORB 時,需要準確地告訴它哪一個類將用作 ORBClass,哪一個類將用作 ORBSingleton 類。我們的實現將不考慮這些,但所有相關的探測則都將考慮這些。正如我前面所說的,這種情況下,我使用的是 Object Oriented Concepts, Inc. 的 Orbacus ORB,而 OOC 類在那兩個 props.put()
調用中已給出。一旦填入了屬性,props
就只作爲一個參數傳遞給 ORB.init()
方法。實際情況可能不是這樣;如果我們要把這個服務器移到另一個 ORB,不希望爲服務器重新編碼。所以,在理想情況下,我們寧願改變一個配置文件,使之指向另一個 ORB 類,然後直接重新啓動。
現在,ORB 已經到位並已初始化,並且實現也已經到位,只是尚未創建,此時,需要爲實現創建一個完善的生存地點,而這可不像聽起來那麼容易,在一個分佈式環境中,各個實現要求的環境可能略有不同。可以賦予實現許多特徵。實現既可以是單線程的,也可以是多線程的;既可以是具有高度可伸縮性的對象池,也可以是單元素。這許多不同的服務器特徵已產生了可移植對象適配器 (POA)。POA 使我們可以創建完善的環境,供我們的實現在其中駐留。所有符合 2.3 規範的 ORB 都會有一個根 POA,所有其它 POA 都是從根 POA 創建的。在這個簡單示例中,我已將實現專用的代碼分解爲它自己的方法 runcalc()
。
爲實現創建一個環境將是我們的第一項任務,所以必須設置一個 POA。本來,CORBA 服務器使用基本對象適配器 (BOA),但是每個供應商的 BOA 都不一樣,在最新版本的 CORBA 規範中,POA 已完全取代了 BOA。
清單 5. SimpleCalcSvr.java -- 設置 POA
|
從標題和定義可以看出,這是一個簡單的示例。使用根 POA 而不創建新的 POA,將使事情變得簡單。POA 管理器是一種封裝了 POA 處理狀態的對象,所以,我們使用 POA 管理器,將發給 servant 的請求排隊。
還需要實例化實現:
清單 6. SimpleCalcSvr.java -- 實例化實現
|
按照 CORBA 2.3 規範,所有 skeleton 均提供一個 _this()
方法,該方法使 servant 能得到目標 CORBA 對象的對象引用,servant 正是用目標 CORBA 對象來與這些請求相關聯的。
完成實現的實例化以後,就必須把機制放到適當的位置,以便客戶機能夠找到它們。有許多不同的方法和服務可用來找到滿足接口請求的對象。CORBA Service 定義 Naming Service 和 Trader Services,來專門幫助客戶機查找對象,以處理請求。也可以通過方法調用來傳遞對象。
在這個示例中,我們將使用所有方法中最直截了當的一種 — 將對象引用寫入一個文件,該文件將由客戶機選取。對於所有的 ORB 來說,創建一個對象引用的字符串表示,或者反過來,創建由字符串到對象的引用,都是必備的功能。
清單 7. SimpleCalcSvr.java -- 編寫對象引用
|
最後要做的一件事,就是激活 POA,使客戶機請求開始排隊,並強制服務器輸入其事件循環,以接收這些傳入的請求。
清單 8. SimpleCalcSvr.java -- 激活 POA
|
首先,我們以相同的方式創建 ORB,就像創建服務器一樣。(您可以下載完整的 文件。)
清單 9. SimpleCalcClient.java -- 初始化 ORB
java.util.Properties props = System.getProperties();
props.put("org.omg.CORBA.ORBClass",
"com.ooc.CORBA.ORG");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
org.omg.CORBA.ORB orb = null;
// 初始化 ORB
orb = ORB.init(args, props);
清單 10. SimpleCalcClient.java -- 獲取對象引用
|
請注意,這裏使用了由 IDL-to-Java 編譯器生成的 calculatorHelper
類。calcref.ior
文件含有一個對象引用,而不是含有計算程序引用。calculatorHelper
類有一個 narrow 方法,可用來將抽象類型集中到特定的計算程序類型。
仔細看一看計算程序 calc
,它表示計算機空間中另外某個地方的一個服務器。最後必須做的一件事,就是調用 calc
上的方法 add()
。
清單 11. SimpleCalcClient.java -- 調用 add()
|
下個月,我們將稍微深入地發掘一下,看一看發生在表面現象之下的 IIOP 的神奇力量。