Plná implementace prostředí CORBA se skládá z prostředí klientského, z prostředí na straně serveru a objektově orientovaných služeb. Hlavní komponenty architektury jsou znázorněny na obrázku. Jednotlivé komponenty mají tyto funkce:
Odvozené typy enum, struct, union, exception a typedef jsou definovány podobně jako v jazyce C, pouze typ union má doplněnu deklaraci rozlišující složky. Navíc jsou zavedeny typy array a sequence, které reprezentují pole o pevné velikosti a dynamické pole. Specializovaný typ string je definován jako sequence<char>. Nejdůležitější součástí jazyka je ovšem deklarace rozhraní (interface), která má formálně stejný tvar jako deklarace třídy v jazyce C++. Rozhraní objektu naší ukázkové aplikace může být definováno v jazyce IDL takto:
module WWW { // deklarace modulu typedef string URL; // adresa stránky typedef sequence<URL> URL_list; // seznam odkazů na stránku exception not_found {}; // deklarace výjimky interface Server { // definice rozhraní objektu boolean reference(in URL url, in string host) raises(not_found); boolean release(in URL url, in string host) raises(not_found); URL_list get_references(in URL url) raises(not_found); }; };Metody definované v rozhraní (interface) reprezentují služby, které objekt poskytuje. Parametry metod musí mít definovány atribut způsobu předávání parametru:
in | parametr je předáván od klienta do implementace objektu |
out | parametr je předáván z implementace objektu klientovi |
inout | parametr je předáván v obou směrech |
Doposud naznačené vlastnosti jazyka IDL nepřináší vcelku nic zásadně nového oproti jiným architekturám volání vzdálených podprogramů (RPC, DCE). Výrazným rozdílem je však princip dědičnosti při definici rozhraní. Podobně jako v jazyce C++ lze definovat nové rozhraní odvozením z existujícího a to i s využitím vícenásobné dědičnosti. Strom dědičnosti definovaný v rozhraní objektu je navíc nezávislý na stromu dědičnosti implementace objektu, takže rozhraní objektu s vícenásobnou dědičností může být implementováno bázovou třídou a naopak.
Definice rozhraní objektu vystupuje v jazyce C++ jako třída. Metody definované v rozhraní jsou pak virtuálními metodami této třídy. Práce s jednoduchými datovými typy je celkem bezproblémová. Jsou mapovány přímo na datové typy jazyka C++ definované jako typedef v modulu CORBA. Například typ boolean jazyka IDL je reprezentován typem CORBA::Boolean. Typ string je přímo mapován na řetězec jazyka C.
Složitější je ovšem mapování složených datových typů. Primární příčinou komplikací je předávání parametrů typu inout a out v metodách objektu. Pokud je parametr výstupní, musí implementační kód alokovat paměť pro uložení hodnoty parametru a tuto paměť pak musí agent ORB bezpečně uvolnit. Všechny složené typy musí proto být mapovány tak, aby bylo zajištěno správné kopírování složek při přiřazování a byla zaručeno uvolnění paměti při vyvolání destruktoru. Pro reprezentaci složených typů union a sequence vygeneruje kompilátor IDL definici třídy, která obsahuje implementaci konstruktoru, přiřazovacího operátoru a destruktoru, resp. další metody pro přístup k prvkům sequence. Mapování struktur a polí závisí na typu prvků a jeho popis se vymyká rozsahu tohoto příspěvku.
Pro bezpečnou manipulaci s ukazateli na složené typy generuje překladač IDL pro každý typ T třídu T_var a T_ptr. Třída T_var zabaluje ukazatel T * a zajišťuje správné chování při přiřazování (zrušení staré hodnoty) a opuštění rozsahu platnosti (uvolnění hodnoty na kterou ukazuje). Přímé použití T * (T_ptr) není omezeno, ale programátor musí sám zajistit explicitně rušení instancí.
Pokud použitý překladač C++ nepodporuje prostory jmen (namespace), je jméno modulu z definice rozhraní v jazyce IDL použito jako předpona mapovaných jmen. Pokud jsou prostory jmen podporovány, odpovídá jméno modulu deklaraci namespace. Pro uvedený příklad by překlad rozhraní do jazyka C++ bez namespace mohl mít tvar:
class WWW_Server: public virtual CORBA_Object { public: virtual CORBA_Boolean reference(const char *url, const char *host, CORBA_Environment& env = _environment) const; virtual CORBA_Boolean release(const char* url, const char *host, CORBA_Environment& env = _environment) const; virtual WWW_URL_list *get_references(const char* url, CORBA_Environment& env = _environment) const; static WWW_Server_ptr _nil() { return &coNilObject; } static WWW_Server_ptr _duplicate(const WWW_Server_ptr obj); static WWW_Server_ptr _narrow(const CORBA_Object_ptr obj); };Skrytou bázovou třídou všech rozhraní je rozhraní objektu CORBA::Object. Operace definované v tomto rozhraní jsou definovány ve standardu CORBA a díky dědičnosti jsou dostupné pro všechna další rozhraní. Jsou to metody pro práci s referencemi na objekty _is_nil() (test platnosti reference) a _release() (uvolnění reference). V příkladu vyznačené statické metody musí být deklarovány znovu pro každou vygenerovanou třídu. Metoda _nil() poskytuje prázdnou referenci odpovídajícího typu, metoda _duplicate() vytváří novou referenci na objekt (inkrementuje počet odkazů) a _narrow() zajišťuje bezpečné přetypování. Další standardní metody pro přístup ke skladu rozhraní (interface repository), skladu implementací (implementation repository) a generování dynamických požadavků již přesahují téma tohoto článku.
Rozhraní objektu CORBA::Object není ve skutečnosti rozhraním vzdáleného objektu, protože objekt je součástí implementace ORB. Podobná jsou rozhraní standardních objektů ORB, BOA, Request, NVList, apod. Tyto objekty jsou proto nazývány ve standardu CORBA pseudo-objekty a jejich rozhraní je definováno v jazyce pseudo-IDL (PIDL). Mapování pseudo-objektů je samozřejmě silně závislé na implementačním jazyku. Nejpřirozenější je pro jazyk C++, kde vystupují pseudo-objekty jako třídy a lze s nimi pracovat podobně jako se vzdálenými objekty.
Na operace s typy mapovanými z jazyka IDL a PIDL jsou vztažena některá omezení. Nejvíce omezení je kladeno na třídy, které reprezentují rozhraní objektu. V programu nesmí být vytvářeny instance, odkazy, ani reference těchto objektů a nesmí být z nich odvozovány nové třídy. Použití rozhraní objektu v jazyce IDL reprezentuje odkaz na objekt (object reference). Odkazy na objekty mohou být předávány v parametrech metod a mohou být také výsledkem. Mezi klientem a serverem je předávána pouze identifikace objektu, vlastní objekt se nepřenáší. Pro práci s odkazy na objekt s rozhraním A v jazyce C++ jsou vygenerovány třídy A_var a A_ptr (obvykle odpovídá A *). Tyto typy jsou podobné typům T_var a T_ptr pro práci s ukazateli na složené typy, liší se ale způsobem vytváření a kopírování objektů:
typedef WWW_Server *WWW_Server_ptr; // A * class WWW_Server_var { // A_var WWW_Server_ptr ptr_; // zabalený ukazatel public: WWW_Server_var(): WWW_Server::_nil()) { } // prázný odkaz WWW_Server_var(WWW_Server *ptr): ptr_(ptr) { } WWW_Server_var(const WWW_Server_var &var): // kopie odkazu ptr_(WWW_Server::_duplicate(var.ptr_)) { } ~WWW_Server_var() { CORBA_release(ptr_); } // uvolnění odkazu WWW_Server_var &operator =(WWW_Server_var &var) { if (ptr_ != var.ptr_) { // identita? CORBA_release(ptr_); // uvolnění odkazu ptr_ = WWW_Server::_duplicate(var.ptr_); // kopie } return *this; } ... operator WWW_Server_ptr() const { return ptr_; } WWW_Server_ptr operator-> const { return ptr_; } };V implementaci konstruktoru objektu a přiřazení lze vidět, jak jsou vytvářeny a rušeny reference na vzdálený objekt. Operace CORBA_release() a CORBA_duplicate() jsou standardními operacemi modulu CORBA pro zrušení reference na objekt a získání nové reference.
Typ zprávy | zdroj | popis |
Požadavek (Request) | klient | vyvolání metody vzdáleného objektu |
Odpověď (Reply) | server | výsledek vyvolání |
Zrušení požadavku (CancelRequest) | klient | zrušení předcházejícího požadavku |
Vyhledání (LocateRequest) | klient | zjištění umístění vzdáleného objektu |
Odpověď na vyhledání (LocateReply) | server | indikuje, zda server implementuje objekt, nebo předá volání dále |
Uzavření spojení (CloseConnection) | server | uzavření spojení |
Chyba (MessageError) | oba | reakce na předchozí zprávu |
Pro reprezentaci složených typů jazyka IDL jsou stanovena přesná pravidla, například posloupnost začíná vždy délkou typu long a pak následují prvky posloupnosti. Při odesílání parametrů požadavku na straně klienta a dekódování parametrů na straně serveru je využito znalosti typů přenášených dat z definice rozhraní objektu v jazyce IDL. Vlastní vkládání a dekódování řeší vygenerované metody v zástupné třídě u klienta nebo kostře na straně serveru. Například vygenerovaná metoda reference() by mohla mít tvar:
CORBA_Boolean WWW_Server::reference(const char* url, const char *host, CORBA_Environment& _env) { CORBA_Boolean _returnValue = 0; Request _req(this, operations, 1); // inicializace zprávy size_t _inSize = _req.size_string(url); // délka hodnoty 1. par. _inSize += _req.size_string(host); // délka 2. parametru _req.initialize(_inSize, 0, _env) != C_OK) { return _returnValue; // není místo pro parametry } _req.put_string(url); // uložení 1. parametru _req.put_string(host); // uložení 2. parametru if (_req.invoke(_env) != C_OK) return _returnValue; // aktivace return _req.get_boolean(); // dekódování výsledku }Složitější je ovšem přenos hodnot typu Any. Skutečná hodnota může být libovolného typu a proto musí být jednak dostupné typové informace k předávané hodnotě na straně odesílatele, jednak musí být tyto informace přeneseny v požadavku nebo odpovědi na stranu příjemce. K tomuto účelu je použito identifikace typu (TypeCode). Protože hodnota identifikace typu je opět předem neznámým typem, je pro přenos identifikace typu využito zapouzdřeného CDR. Takto přenášené hodnota musí v sobě obsahovat dostatečné informace pro správné dekódování. Reprezentace spočívá v posloupnosti oktetů, je tedy stejná jako pro standardní typ IDL sequence<octet>. Navíc je před začátkem posloupnosti indikace formátu slabik ve slově.
typedef unsigned long ProfileId; const ProfileId TAG_INTERNET_IOP = 0; struct TaggedProfile { // profil ProfileId tag; // typ profilu sequence <octet> profile_data; // vlastní adresa }; struct IOR { string type_id; // typ rozhraní objektu sequence <TaggedProfile> profiles; // seznam profilů (adres) };První složka type_id obsahuje identifikaci rozhraní zpřístupňovaného objektu. Obsah profilu je obecně závislý na protokolu komunikace mezi ORB. Obecný protokol GIOP doporučuje, aby profil obsahoval alespoň číslo verze protokolu, adresu na úrovni komunikačního protokolu a klíč (object_datum) pro identifikaci objektu. Profil pro odkaz na úrovni protokolu IIOP je definován takto:
struct Version { char major; // '1' char minor; // '0' }; struct ProfileBody { Version iiop_version; string host; // doménové jméno nebo IP adresa unsigned short port; // číslo portu sequence <octet>object_key; // závisí na OA a ORB };Pro manipulaci s odkazy jsou v rozhraní objektu ORB definovány operace pro převod IOR na řetězec ORB::object_to_string() a opačně ORB::string_to_object(). Převod na řetězec využívá zapouzdřenou reprezentaci CDR se zápisem oktetů v hexadecimálním kódu. Pro identifikaci je navíc na začátku prefix IOR.
IOR:010000002800000049444C3A6F6D672E6F72672F436F734E616D696E672F4E616D 696E67436F6E746578743A312E30000100000000000000540000000101000012000000 626F636F2E6665652E76757462722E637A008913340000004F422F49442B4E554D0049 444C3A6F6D672E6F72672F436F734E616D696E672F4E616D696E67436F6E746578743A 312E30003000
#include "WWW.h" // vložení vygenerovaného hlavičkového souboru int main(int argc, char **argv) { CORBA_Environment env; CORBA_ORB_ptr orb = CORBA_ORB_init(argc, argv, 0, env); if (env.exception()) { cerr << "Cannot initialize ORB\n"; return 1; }Výsledkem funkce ORB_init() je odkaz na pseudo-objekt reprezentující rozhraní ORB. V příkladu je použito nejjednodušší možné implementace ORB v jazyce C++ bez namespace a výjimek. Chybové stavy jsou v tomto případě signalizovány vv prostředí, které je předáváno v posledním parametru funkcí modulu CORBA.
Dalším krokem je získání odkazu na objekt, který chce klient využívat. Nejjednodušší možný postup je získání reference ze standardního vstupu programu:
cin.getline(sior, sizeof(sior)); // přečíst řádek ze vstupu CORBA_Object_var obj = orb->string_to_object(sior); if (CORBA_is_nil(obj)) { // je adresa platná? cerr << "Bad IOR\n"; return 1; } WWW_Server_var server = WWW_Server::_narrow(obj);// přetypování if (CORBA_is_nil(server)) { // je typ objektu OK? cerr << "bad interface imported\n"; return 1; } // použití vzdáleného objektu server->reference("/index.html", hostname, &env); if (env.exception()) { ... // chyba při volání }Po získání reference na objekt následuje přetypování na požadovaný typ metodou _narrow(). Tato metoda ověří, zda je získaná reference skutečně odkazem na objekt požadovaného rozhraní. Získaná proměnná server je ukazatelem na objekt zástupné třídy. Prostřednictvím metod tohoto objektu může klient transparentně používat služby vzdáleného objektu.
CORBA_Object_ptr ns_obj; try { // získání objektu NS ns_obj = orb->resolve_initial_references("NameService"); } catch(...) { cerr << "cannot resolve NameService" << endl;return 1; } CosNaming_NamingContext_var nc; // přetypování na kontext nc = CosNaming_NamingContext::_narrow(ns_obj) CosNaming_Name name; // jméno hledaného objektu name.length(1); // nastavení délky posloupnosti name[0].id = CORBA_string_dup("WWW_Server"); name[0].kind = CORBA_string_dup(""); CORBA_Object_var obj; try { obj = nc->resolve(name); // vyhledání objektu a } catch(...) { // získání reference cerr << "cannot resolve WWW_Server" << endl; return 1; }Po získání adresy rozhraní objektu NameService následuje vytvoření pseudo-objektu CosNaming::Name, který popisuje hledaný objekt. Hledání může probíhat v různých kontextech určených objektem CosNaming::NamingContext. V tomto případě je pro jednoduchost použit základní kořenový kontext. Výsledkem vyhledání objektu metodou resolve() je opět reference na hledaný objekt, kterou dále přetypujeme a používáme stejně jako v předchozím příkladu.
Druhá metoda spočívá v delegování metod z kostry objektu do implementačního objektu. V tomto případě je závislost opačná, kostra objektu částečně závisí na implementační třídě (ve skutečnosti pouze na jejím jménu). Implementační třída pak nemá žádné omezení z hlediska dědičnosti. Příkladem může být implementace COOL ORB firmy Chorus. Implementační třída našeho příkladu je v obou případech podobná, u druhé metody by pouze nebyla odvozena od vygenerované třídy WWW_Server_skel (název kostry se samozřejmě u každé implementace liší):
class ServerImpl: WWW_Server_impl { struct sless { // operátor less<const char *> bool operator() (const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } }; typedef list<const char *> ref_list; // seznam STL typedef map<const char *, ref_list, sless> ref_map; // asociativní pole ref_map references; // seznam odkazů public: virtual CORBA_Boolean reference(const char *url, const char *host, CORBA_Environment &env); virtual CORBA_Boolean release(const char *url, const char *host, CORBA_Environment &env); virtual WWW_URL_list *get_references(const char *url, CORBA_Environment &env); };Privátní část třídy obsahuje deklarace potřebné pro implementaci objektu, v daném případě seznam a pole ze standardní knihovny šablon (STL) jazyka C++. Deklarace metod odpovídá vygenerované kostře objektu. Dalším krokem je implementace metod, například reference() :
CORBA_Boolean ServerImpl::reference(const char *url, const char *host, CORBA_Environment&) { host = CORBA_string_dup(host); // kopie hodnoty if (references.find(url) == references.end()) { // není zde references[CORBA_string_dup(url)].push_back(host); return CORBA_TRUE; } references[url].push_back(host); // přidat další odkaz return CORBA_FALSE; }Vstupní parametry jsou vždy předávány jako const a jejich hodnoty jsou zaručeny pouze po dobu volání metody. Pokud má být hodnota parametru uchována, je třeba ji okopírovat (v tomto případě CORBA_string_dup()). Naopak hodnoty výstupních parametrů a výsledek musí implementace alokovat příkazem new nebo funkcí CORBA_string_alloc() pro řetězce a ORB je po odeslání uvolní příkazem delete, resp. CORBA_string_free().
Kostra objektu dekóduje přicházející požadavky inverzním postupem oproti zástupné třídě u klienta a volá metody implementační třídy:
case id_reference: // příklad zpracování volání metody reference WWW_URL url = 0; char *host = 0; _req.get_string(url); // dekódování 1. parametru _req.get_string(host); // dekódování 2. parametru CORBA_Boolean _rValue; _rValue = reference(url, host, _env); // vyvolání implementace // inicializace odpovědi if (_req.initialize(1, _env) != C_OK) return C_ENOMEM; _req.put_Boolean(_returnValue); // uložení výsledku CORBA_string_free(url); // uvolnění alokované hodnoty CORBA_string_free(host); return C_OK; // návrat a odeslání odpovědi
int main(int argc, char **argv) { CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); CORBA::BOA_var boa = orb->BOA_init(argc, argv); ServerImpl serverImpl; // vytvoření instance objektu cout << orb->object_to_string(server) << endl; // výpis IOR boa->impl_is_ready(CORBA_ImplementationDef::_nil()); }Tato automatická vazba ale není zaručena, u některých implementací je požadována explicitní registrace. Při použití metody delegování musí být navázání vždy explicitní. Vazba objektu na objektový adaptér je jedním ze slabých míst standardu CORBA 2.0 a proto je v současné době připravována nová specifikace přenositelného objektového adaptéru (POA), která by měla uvedené problémy vyřešit.
Po inicializaci objektového adaptéru následuje vytvoření instance implementačního objektu a výpis jeho IOR na standardní výstup. Poslední volaná metoda signalizuje objektovému adaptéru, že implementace je připravena přijímat a zpracovávat požadavky. V daném případě se předpokládá, že program bude spuštěn před příchodem prvního požadavku. Základní objektový adaptér (BOA) by měl dle standardu CORBA podporovat tyto čtyři režimy aktivace objektů:
Sdílený server | Sdílený server je spuštěn mechanismem ORB pouze jednou. Po své inicializaci signalizuje připravenost metodou impl_is_ready() a sekvenčně zpracovává všechny požadavky na objekty implementující dané rozhraní. Je typický pro složité služby a persistentní objekty. |
Trvalý server | Trvalý server je spuštěn mimo prostředí ORB, jinak je jeho funkce stejná jako u sdíleného serveru. |
Nesdílený server | Nesdílený server obsluhuje pouze jeden objekt implementující dané rozhraní. Je aktivován při požadavku klienta na objekt. |
Server na požadavek | Server je spuštěn při každém novém požadavku na objekt. Po zpracování požadavku běh serveru končí. |
Název služby | ftp.omg.com/
docs/formal/ |
Stručný popis funkce |
Naming Service | 97-02-08.pdf | registrace IOR a zpřístupnění jmény objektů |
Event Service | 97-02-09.pdf | zasílání asynchronních událostí |
Persistent Object Service | 97-02-10.pdf | ukládání objektů do souborů a databází |
Life Cycle Service | 97-02-11.pdf | vytváření a rušení objektů |
Concurrency Control | 97-02-12.pdf | paralelní přístup k objektům, synchronizace |
Externalization Service | 97-02-13.pdf | ukládání a obnovení stavu objektů |
Relationship Service | 97-02-14.pdf | vztahy mezi objekty, ekvivalence objektů |
Transaction Service | 97-02-15.pdf | transakce |
Query Service | 97-02-16.pdf | vyhledávání v souborech objektů, SQL, OQL |
Licensing Service | 97-02-17.pdf | správa licencí |
Property Service | 97-02-18.pdf | vlastnosti objektů |
Property Service IDL | 97-02-19.pdf | rozšíření IDL pro definice vlastností objektů |
Security Service | 97-02-20.pdf | ochrana a bezpečnost, SCIOOP |
Time Service Spec. | 97-02-22.pdf | generování událostí v čase, měření intervalů |
Firma | Produkt | URL | Objektové služby |
Chorus | COOL/ORB | www.chorus.com/Products/Cool | NS |
Digital | ObjectBroker | www.digital.com/info/objectbroker/ | NS |
ExperSoft | CORBAPlus | www.expersoft.com/ | NS, Event, Rel, Pers, |
Gerald Brose | JacORB | www.inf.fu-berlin.de/~brose/jacorb/ | NS, Event, zdroj zdarma |
HP | ORB Plus | www.hp.com/gsy/orbplus.html | NS, LC, Event |
IBM | Component Broker | www.software.ibm.com/ad/cb/ | NS, LC, Event, Pers, Trans, Sec, Conc |
ICL | DAIS | www.icl.com/products/dais/ | NS, LC, Sec, Trans |
IONA | Orbix | www.iona.com/Products/Orbix/ | NS, Event, Sec, Trans |
Olivetti/
Oracle |
OmniOrb2 | www.orl.co.uk/omniORB/
omniORB.html |
NS, nemá Any a IR, zdroj zdarma |
OO Concepts | OmniBroker | www.ooc.com/ob.html | NS, zdroj zdarma |
Sun | IIOP | ftp.omg.org/pub/interop/iiop.tar.Z | implementace IIOP/C++ |
NEO | www.sun.com/solaris/neo | NS, LC, Prop, Event | |
JavaIDL | www.sun.com/sunsoft/solaris/java-idl/ | NS | |
Visigenic | VisiBroker | www.visigenic.com/prod/ | NS, Events, LC |
CORBAservices: Common Object Service Specification, OMG, 1996
ORB Portability Joint Submission, OMG, May 1997
COOL-ORB Tutorial, Chorus Systems, 1997
The Orbix Architecture, IONA Technologies, November 1996
VISIGENIC VisiBroker for C++ Programmer s Guide Version 2.0, Visigenic, 1997