這兒顯示的代碼可能並不詳盡,因為不同的ORB有不同的方法來訪問CORBA服務,所以無論什麼例子都要取決於具體的廠商(下例使用了JavaIDL,這是Sun公司的一個免費產品。它配套提供了一個簡化版本的ORB、一個命名服務以及一個“IDL→Java”編譯器)。除此之外,由於Java仍處在發展初期,所以在不同的Java/CORBA產品裡並不是包含了所有CORBA特性。
我們希望實現一個服務器,令其在一些機器上運行,其他機器能向它查詢正確的時間。我們也希望實現一個客戶,令其請求正確的時間。在這種情況下,我們讓兩個程序都用Java實現。但在實際應用中,往往分別采用不同的語言。
1. 編寫IDL源碼
第一步是為提供的服務編寫一個IDL描述。這通常是由服務器程序員完成的。隨後,程序員就可用任何語言實現服務器,只需那種語言裡存在著一個CORBA IDL編譯器。
IDL文件已分發給客戶端的程序員,並成為兩種語言間的橋梁。
下面這個例子展示了時間服務器的IDL描述情況:
module RemoteTime { interface ExactTime { string getTime(); }; };
這是對RemoteTime命名空間內的ExactTime接口的一個聲明。該接口由單獨一個方法構成,它以字串格式返回當前時間。
2. 創建根干
第二步是編譯IDL,創建Java根干代碼。我們將利用這些代碼實現客戶和服務器。與JavaIDL產品配套提供的工具是idltojava:
idltojava -fserver -fclient RemoteTime.idl
其中兩個標記告訴idltojava同時為根和干生成代碼。idltojava會生成一個Java包,它在IDL模塊、RemoteTime以及生成的Java文件置入RemoteTime子目錄後命名。_ExactTimeImplBase.java代表我們用於實現服務器對象的“干”;而_ExactTimeStub.java將用於客戶。在ExactTime.java中,用Java方式表示了IDL接口。此外還包含了用到的其他支持文件,例如用於簡化訪問命名服務的文件。
3. 實現服務器和客戶
大家在下面看到的是服務器端使用的代碼。服務器對象是在ExactTimeServer類裡實現的。RemoteTimeServer這個應用的作用是:創建一個服務器對象,通過ORB為其注冊,指定對象引用時采用的名稱,然後“安靜”地等候客戶發出請求。
import RemoteTime.*; import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.CORBA.*; import java.util.*; import java.text.*; // Server object implementation class ExactTimeServer extends _ExactTimeImplBase{ public String getTime(){ return DateFormat. getTimeInstance(DateFormat.FULL). format(new Date( System.currentTimeMillis())); } } // Remote application implementation public class RemoteTimeServer { public static void main(String args[]) { try { // ORB creation and initialization: ORB orb = ORB.init(args, null); // Create the server object and register it: ExactTimeServer timeServerObjRef = new ExactTimeServer(); orb.connect(timeServerObjRef); // Get the root naming context: org.omg.CORBA.Object objRef = orb.resolve_initial_references( "NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); // Assign a string name to the // object reference (binding): NameComponent nc = new NameComponent("ExactTime", ""); NameComponent path[] = {nc}; ncRef.rebind(path, timeServerObjRef); // Wait for client requests: java.lang.Object sync = new java.lang.Object(); synchronized(sync){ sync.wait(); } } catch (Exception e) { System.out.println( "Remote Time server error: " + e); e.printStackTrace(System.out); } } }
正如大家看到的那樣,服務器對象的實現是非常簡單的;它是一個普通的Java類,從IDL編譯器生成的“干”代碼中繼承而來。但在與ORB以及其他CORBA服務進行聯系的時候,情況卻變得稍微有些復雜。
4. 一些CORBA服務
這裡要簡單介紹一下JavaIDL相關代碼所做的工作(注意暫時忽略了CORBA代碼與不同廠商有關這一事實)。main()的第一行代碼用於啟動ORB。而且理所當然,這正是服務器對象需要同它進行溝通的原因。就在ORB初始化以後,緊接著就創建了一個服務器對象。實際上,它正式名稱應該是“短期服務對象”:從客戶那裡接收請求,“生存時間”與創建它的進程是相同的。創建好短期服務對象後,就會通過ORB對其進行注冊。這意味著ORB已知道它的存在,可將請求轉發給它。
到目前為止,我們擁有的全部東西就是一個timeServerObjRef——只有在當前服務器進程裡才有效的一個對象引用。下一步是為這個服務對象分配一個字串形式的名字。客戶會根據那個名字尋找服務對象。我們通過命名服務(Naming Service)完成這一操作。首先,我們需要對命名服務的一個對象引用。通過調用resolve_initial_references(),可獲得對命名服務的字串式對象引用(在JavaIDL中是“NameService”),並將這個引用返回。這是對采用narrow()方法的一個特定NamingContext引用的模型。我們現在可開始使用命名服務了。
為了將服務對象同一個字串形式的對象引用綁定在一起,我們首先創建一個NameComponent對象,用“ExactTime”進行初始化。“ExactTime”是我們想用於綁定服務對象的名稱字串。隨後使用rebind()方法,這是受限於對象引用的字串化引用。我們用rebind()分配一個引用——即使它已經存在。而假若引用已經存在,那麼bind()會造成一個異常。在CORBA中,名稱由一系列NameContext構成——這便是我們為什麼要用一個數組將名稱與對象引用綁定起來的原因。
服務對象最好准備好由客戶使用。此時,服務器進程會進入一種等候狀態。同樣地,由於它是一種“短期服務”,所以生存時間要受服務器進程的限制。JavaIDL目前尚未提供對“持久對象”(只要創建它們的進程保持運行狀態,對象就會一直存在下去)的支持。
現在,我們已對服務器代碼的工作有了一定的認識。接下來看看客戶代碼:
import RemoteTime.*; import org.omg.CosNaming.*; import org.omg.CORBA.*; public class RemoteTimeClient { public static void main(String args[]) { try { // ORB creation and initialization: ORB orb = ORB.init(args, null); // Get the root naming context: org.omg.CORBA.Object objRef = orb.resolve_initial_references( "NameService"); NamingContext ncRef = NamingContextHelper.narrow(objRef); // Get (resolve) the stringified object // reference for the time server: NameComponent nc = new NameComponent("ExactTime", ""); NameComponent path[] = {nc}; ExactTime timeObjRef = ExactTimeHelper.narrow( ncRef.resolve(path)); // Make requests to the server object: String exactTime = timeObjRef.getTime(); System.out.println(exactTime); } catch (Exception e) { System.out.println( "Remote Time server error: " + e); e.printStackTrace(System.out); } } }
前幾行所做的工作與它們在服務器進程裡是一樣的:ORB獲得初始化,並解析出對命名服務的一個引用。
接下來,我們需要用到服務對象的一個對象引用,所以將字串形式的對象引用直接傳遞給resolve()方法,並用narrow()方法將結果造型到ExactTime接口引用裡。最後調用getTime()。
5. 激活名稱服務進程
現在,我們已分別獲得了一個服務器和一個客戶應用,它們已作好相互間進行溝通的准備。大家知道兩者都需要利用命名服務綁定和解析字串形式的對象引用。在運行服務或者客戶之前,我們必須啟動命名服務進程。在JavaIDL中,命名服務屬於一個Java應用,是隨產品配套提供的。但它可能與其他產品有所不同。JavaIDL命名服務在JVM的一個實例裡運行,並(默認)監視網絡端口900。
6. 激活服務器與客戶
現在,我們已准備好啟動服務器和客戶應用(之所以按這一順序,是由於服務器的存在是“短期”的)。若各個方面都設置無誤,那麼獲得的就是在客戶控制台窗口內的一行輸出文字,提醒我們當前的時間是多少。當然,這一結果本身並沒有什麼令人興奮的。但應注意一個問題:即使都處在同一台機器上,客戶和服務器應用仍然運行於不同的虛擬機內。它們之間的通信是通過一個基本的集成層進行的——即ORB與命名服務的集成。
這只是一個簡單的例子,面向非網絡環境設計。但通常將ORB配置成“與位置無關”。若服務器與客戶分別位於不同的機器上,那麼ORB可用一個名為“安裝庫”(Implementation Repository)的組件解析出遠程字串式引用。盡管“安裝庫”屬於CORBA的一部分,但它幾乎沒有具體的規格,所以各廠商的實現方式是不盡相同的。
正如大家看到的那樣,CORBA還有許多方面的問題未在這兒進行詳細講述。但通過以上的介紹,應已對其有一個基本的認識。若想獲得CORBA更詳細的資料,最傳真的起點莫過於OMB Web站點,地址是http://www.omg.org。這個地方提供了豐富的文檔資料、白頁、程序以及對其他CORBA資源和產品的鏈接。