如果你已在使用OOP,那CORBA使你編寫分布式應用程序變得容易,因為它讓你象使用本地對象一樣使用遠程對象。這是因為CORBA應用程序的設計與任何其它OO應用程序非常相似,除了它多出一層,該層當一個對象駐留在另外一台機器上時管理網絡通信。這個多出來的層由兩個特殊的對象管理:存根和框架(stub&skeletons),如下圖所示。
在CORBA客戶端,存根充當某個對象的代理,該對象可能實現在同一進程、另一進程甚至另一台計算機(服務器)中。客戶與存根打交道,好象存根就是那個對象一樣。
然而,不象其它大多數對象,存根通過調用安裝在客戶機上的ORB軟件來管理界面調用。VisiBroker ORB使用在局域網某處中運行著的智能代理(osagent),智能代理是一個動態分布目錄服務,目錄服務能夠定位提供對象實現的服務器。
在CORBA服務器端,ORB軟件將界面調用傳給一個自動生成的骨架。這個骨架對過基本對象配適器(Basic Object Adaptor)與ORB軟件通信。通過使用BOA,骨架將對象注冊到智能代理,說明對象的范圍(即它對遠程計算機是否可用)及對象是何時實例化並准備好響應客戶的。
以下的論題說明如何用這個基本模型去匹配你的分布應用程序需要。 理解存根與骨架 使用智能代理 激活服務器應用程序 動態綁定界面調用
理解存根與骨架
CORBA中分布對象的基礎是它的界面。界面很象類定義,除了它不包含實現信息。用CORBA的界面定義語言(IDL)定義界面。然後就可以把界面定義作為一個IDL文件加進項目中。關於IDL、STUB、SKELETON的進一步信息見VisiBroker編程指南。對於Cbuilder中使用IDL文件的信息見 定義對象界面。
當你編譯IDL文件時,CBUILDER為你建立兩個.cpp文件。一個是客戶文件,包含存根的實現;另一個是服務器文件,包含骨架類的實現。存根和框架提供了允許CORBA應用程序套接解析(Marshaling)界面調用的機制。Marshaling: 取服務器進程中的一個對象實例使之對客戶進程代碼可用。 傳遞界面調用的參數,即將客戶傳來的參數放進遠程對象進程空間裡。
當客戶應用調用CORBA對象的方法時,它將參數壓進棧中然後調用存根對象。存根將參數寫進marshaling緩沖區,然後用一個結構(structure)將調用改送給遠程對象。服務器框架解開這個結構,將參數壓進棧,然後調用對象實現。簡單點說,框架在自己的地址空間裡重建客戶調用。
使用智能代理
智能代理(osagent)是一個動態的分布式目錄服務,該服務定位實現特定對象的有效服務器。如果有多個服務器供選,智能代理提供載入平衡。它還提供服務器失敗保護,方式是在鏈接失敗時嘗試重起服務或必要時定位到其它主機上的服務器。
智能代理必須在局域網中的至少一台主機上啟動,這裡的局域網是指一個廣播消息可以被送達范圍的網絡。ORB用廣播消息定位智能代理。如果網絡中有多個智能代理,ORB使用第一個響應的。一旦定位到一個智能代理,ORB使用點對點UDP協議與之通信。UDP協議占用的資源比TCP連接少。
當一個網絡包括多個智能代理時,每個智能代理識別有效對象的一個部分,並與其它代理通信讓它們定位其接直識別的對象。如果一個智能代理意外結束,其它代理會自動重新登記它所保持跟蹤的對象。
在局域網中使用和配置智能代理的細節,見VisiBroker安裝與管理指南。
激活服務器應用程序
當服務器啟動時,它會告知可接受用戶調用的對象的ORB(通過基本對象配適器)。初始化ORB並告訴它服務器已經起動就緒的代碼會被向導(你用來生成CORBA服務器程序的那個)自動加進你的應用程序裡。
通常,CORBA服務器程序要自動啟動。然而你可以用對象激活守護進程(Object Activation Daemon)在客戶需要使用時啟動你的服務器或只實例化它們的對象。
要使用OAD,必要在它上面注冊你的對象。當對象已經注冊到OAD之後,它將對象及實現這些對象的服務的對應關系保存在數據庫中,這個庫稱為Implemnetation Repository(實現倉庫)。
一旦實現倉庫中有對象的入口,OAD將為ORB模擬你的程序(服務器)。當客戶請求這個對象時,ORB與OAD聯系就好象它就是服務器程序一樣。OAD接著將客戶請求轉給真正的服務器,必要時啟動這個應用程序(服務器)。
在OAD中注冊對象的細節見VisiBroker Programmers Guide。
動態綁定界面調用
通常CORBA客戶在調用服務器上的對象界面時使用靜態綁定。這種方法有許多好處,包括更快速的性能、編譯時類型檢查。然而,有時會直到運行時才知道想用什麼界面。這時,Cbuilder允許你運行時動態綁定界面。
當使用動態綁定時,將界面注冊到界面倉庫(Interface Repository)中是有幫助的。
在客戶程序中如何使用動態綁定,參見VisiBroker Programmers Guide的Dynamic Invocation Interface (DII)部分。
[ 返回 ]
定義對象界面
CORBA對象界面是用IDL(界面定義語言)定義的。IDL的語法類似於C++,因此IDL文件看起來類似於C++頭文件。IDL文件的作用也十分類似於C++頭文件,說明可以共享的界面,正如C++頭文件說明可以共享的類。然而CORBA中的界面(類)是在不同應用程序間共享的,這與C++類在同一應用程序的不同模塊間共享是有區別的。
注意:術語IDL在不同的上下文中所指的都是但彼此不並是同一種的interface definition languages。實際上有CORBA IDL(OMG定義) Microsoft IDL(用於COM)和DCE IDL。CORBA IDL語言的詳情見CORBA IDL 語法。
要想在Cbuilder中定義一個新的IDL文件,選擇File|New菜單,再從對話框的Multitier page頁中選擇CORBA IDL。這將導致用編輯器打開一個空白IDL文件,並且這個文件自動被加到當前工程項目中(project)。
如果你已經有一個定義對象的IDL文件,只要簡單的將它加到項目中來:Project|Add to Project,文件類型選為IDL,選取已經存在的那個IDL文件。
IDL語法
CORBA IDL是由對象管理組織(Object Management Group)為定義所有的CORBA界面而制定的。這裡提供一個IDL的浏覽,讓你能夠建立IDL文件定義大多數的對象。最新的IDL完整定義見OMG的Web站點([email protected])。
你可以用IDL定義types(類型),constans(常量)和interfaces(界面)。這與C++中定義類型、常量和類相似。IDL是定義界面和類型的語言,它沒有供你編寫實現部分的元素。界面定義在IDL文件中的對象必須實現在分離的實現單元中。
IDL文件的標識符包括英文字母、數字和下劃線,但不可以用下劃線開頭。IDL是大小寫敏感的。然而,避免使用下劃線通常是個好主意,因為將IDL翻譯到不支持下劃線的語言時會出問題。類似的,僅用大小寫區標識符也不好,這在大小寫不敏感的語言中會引起問題。
IDL文件支持以下預編譯指令:
#define
#elif
#else
#endif
#ifdef
#ifndef
#include
例如,只有在IDL文件包含以下預編譯指令時:
#include <orb.idl>
才可以用CORBA偽類型NamedValue, Principal和TypeCode定義界面名。
預編譯指令細節見VisiBroker文檔。
類型:與C++類似IDL中各種成員都要指定其類型。
Typedefs:可以用來給任何類型指定名字,如
typedef string MemoVal;
它對於基本類型或結構類型來說不是必需的(它們在定義中已經命名),但對數組和模板類型是必要的。
基本類型
下表為IDL支持的基本類型。注意沒有int類型並且char類型等價於unsigned(字母是無所謂正負的)
類型 說明
float IEEE單精度浮點數
double IEEE雙精度浮點數
long 32位有符號整數
short 16位有符號整數
unsigned long 32位無符號整數
unsigned short 16位無符號整數
char 8位字符(ASCII的ISO-Latin子集)
boolean 布爾型(真或假)
octet 8位8位數保證傳遞過程中不變(char不是)
any 任意IDL類型(類似Variant型)
NamedValue 成對的一個名字(string)和一個值(any型)
數組(Arrays)
IDL提供多維定長數組以存放同類成員。每一維的長度都必須在定義中說明。例如:
typedef long CellValues [10][20];
注意:必須加一個typedef,如果數組用作參數、屬性或返回值
模板類型(Template types)
IDL提供兩個模板類型:sequence(序列)和string(字符串)。象數組一樣,要用typedef指定sequence和string類型的名字
一個序列是一個可變長度成員清單,成員可以為任何IDL類型。它象一維數組,但長度是不定的。它可以是有界的也可以是無界的,要看序列類型定義時指定的最大長度。例如,下面定義一個有界序列類型:
typedef sequence <octet, 10> UpToTenByte;
這個類型的實例是一個長度小於等10的字節序列。
下一行定義了一個無界限的字節序列類型:
typedef sequence <octet> SomeBytes;
這個類型的實例可以是任意長度的字節序列。
string是一個字符序列。象序列一樣,它可以是有界的:
typedef string<15> Moniker;
或無界的:
typedef string Description;
當定義一個有界字符串類型時,記住任何null結束符不算在串長度之內。使用string面不要用字符數組。字符數組中未初始化的成員在翻譯過程中可能引起問題。
結構類型
IDL文件也可以定義結構類型,用關鍵字struct, union和enum。
下邊有一個結構類型定義的例子:
struct StructName {
char charMember;
unsigned short AnotherMember;
};
注意:這裡沒有typedef。對struct用typedef是不好的習慣,因為它會定義兩個類型名字。
enum(枚舉)類型定義示例:
enum Pet {cat, dog, fish, bird, rat, horse, gerbil};
IDL中的union必須是可區分(discriminated)的。即,用一個標記字段(tag field)說明聯合的哪些成員是當前被賦值的:
union Reference switch (short) {
case 1 : { Title: string; Author: string; }
case 2: URL: string;
case 3: TopicID: long;
};
常量
IDL文件可以定義常量,這些常量可用於界面和類型的定義。用關鍵字const定義常量:
const unsigned long LengthOfNameString = 15;
IDL文件可以定義類型為long, unsigned long, unsigned short,char boolean,float,double,string的常量。注意,IDL不支持octet型的常量。
整數常量可以用10、8、16進制形式說明。字符常量定義可以使用標准的C++轉義序列,如\n \t等。
界面
IDL文件中的界面描述了CORBA對象提供的功能。它封裝客戶使用界面所需的全部信息。每個界面對應CORBA服務器上的一個類,其定義近似於類的定義:
interface Example1 {
readonly attribute string Name;
attribute long Value;
long AddToValue(in long Summand, out long Result);
};
界面用關鍵字interface定義。在界面定義內部是一個屬性和方法的清單。所有的屬性和方法都是公開的(public)。這裡沒有私有(private)或保護(protected)的概念,因為這些應該在實現部分處理而不應在對象的公開界面裡。
屬性
界面沒有成員變量。屬性(Attributes)象Cbuilder的property(這種說法並不暗示值的存儲方法相同)而不象成員變量。用關鍵字attribute定義。
如果不允許客戶寫屬性,定義時要加readonly前綴。
Readonly attribute float Balance;
注意:盡管attribute的行為表現得象properties,但它們並不是用Cbuilder的properties(不可移植)實現的。相反,屬性(attributes)被編譯成與屬性同名的getter(及可選的setter)方法。
方法
方法定義必須包括返回值類型。
另外,它們必須在每個參數中用關鍵字in,out,inout提供用法說明,每個參數都必須命名。
通常,方法調用在服務器對象處理調用時是阻塞的(blocked)。然而,IDL語法允許一個方法申明為oneway,如果調用者確實不需要等待響應的話。
Oneway void SendInformation(long Value);
重復的方法名是非法的,因為CORBA是可用於支持方法重載的編程語言的。
注釋:
界面中可以加注釋,用和C++相同的//。
// this is a comment
類型定義
界面可以包括類型定義:
interface Example2 {
struct Example2Struct {
string Name;
long Value;
};
// …其它定義部分
};
在同一界面中的定義可以用名字(Example2Struct)引用這些類型,但該界面外的定義必須使用范圍限定符(Example2::Example2Struct)。
異常
除了attributes, methods, types, 界面還可以包括用戶定義異常處理:
interface ExceptionExample {
exception ValueOutOfBounds {
long value;
void SetValue(in long Value)
raises (ValueOutOfBounds);
}
}
SetValue方法指出它可能拋出ValueOutOfBounds異常。這個例子顯示一個成員的一個異常,它可以幫助調試。然而,界面也可以包括無成員包括的花括號{}裡的異常。
上下文(Contexts)
方法可以附帶context子句。客戶可以維護一個或多個CORBA上下文對象,它提供標識符(indentifier)到字符串(string)值的映射。一個IDL方法可以用關鍵字context申明對特定標識符的映射必須提供給它的客戶:
interface ContextExample {
long SomeMethod(in long Value, in char Letter)
raises (/* */)
context(“value1”, “value2”);
};
};
ContextExample的調用者必須傳送一個context對象作為參數。
模塊(Modules)
IDL文件將一些定義結合成模塊。模塊的概念和名字空間(namespace)是一樣的:它允許定義組合成邏輯單元並預防命名空間沖突。一個模塊定義一個名字空間,就象這樣:
module SyntaxExamples {
interface Example1 {
// definitions here
};
interface Example2 {
// definitions here
};
};
模塊外的定義訪問模塊內的定義必須加范圍界定符(SyntaxExamples::Example1)。
繼承(Inheritance)
正如C++類,IDL界面可以作為其它類的後代而建立。子代界面繼承祖先界面的屬性和方法:
interface Ancestor{
//definitions here
};
interface Desendant:Ancestor {
// more definitions here
};
注意:因為沒有界面相關的實現部分,衍生界面重載相應類方法時不得再在成員函數中列出。
界面支持多繼承,但所有祖先界面都不能有包含相同名字的定義。
所有的IDL界面都是隱含繼承了CORBA界面Object。這意味著一個類型為Object的參數可以接受任何CORBA對象。
提前引用(Forward references)
如果兩個界面互相引用,IDL文件必須一個包含對其中一個的提前引用,即一個界面可以引用IDL文件中其後部分的定義。如下所示:
interface Example2; // forwar reference
interface Example1 {
//definitions
readonly attribute Example2 TheOtherOne;
};
interface Example2 {
//definitions
Example1 ReturnTheOtherOne();
};
提前引用也允許一個界面包含對自己的引用。
摘自:品雪其寒