. Aplicatii Bazate pe Componente Folosind Tehnologia Corba

Aplicații bazate pe componente folosind

tehnologia CORBA

1. Introducere

Dezvoltarea rețelelor de calculatoare și interconectarea lor la nivel global (prin Internet) a pus problema unei comunicații cât mai eficiente între arhitecturi eterogene [5]. Diversitatea partenerilor de discuție este explicată prin procesul natural de dezvoltare a rețelelor de-a lungul timpului.

La ora actuala exista patru mari arhitecturi distribuite:

CORBA (Common Object Request Broker Architecture)

DCE (Distributed Computing Environment)

DCOM (Distributed Component Object)

Java RMI (Remote Method Invocation)

CORBA, un standard elaborat de OMG (Object Management Group), este un cadru de dezvoltare a aplicațiilor distribuite în medii eterogene. La acest proiect au participat toate marile companii de soft, cu excepția Microsoft-ului, care are propriul sau model (DCOM). CORBA se bazează pe OMA (Object Management Architecture), model propus de OMG, pentru transbordarea într-un mediu distribuit a programării orientate pe obiecte, ceea ce implica o dezvoltare rapida, reutilizarea si protecția eficientă a datelor.

În viziunea CORBA, un sistem distribuit este alcătuit din "clienți" ce utilizează diferite obiecte distribuite. Datorită modalităților diverse de comportare a obiectelor în sisteme de operare diferite (în thread-uri, biblioteci cu legare dinamica – DLL-uri ,…), CORBA lucrează cu noțiunea de "server" (fiecare obiect este asociat unui server). Rolul acestuia este de a include implementarea obiectelor asociate. Acest model impune doar invocarea de către clienți a obiectelor de pe server și nu a serverului însuși. Asemeni tuturor modelelor, standardelor si produselor software, CORBA evoluează. Experiența câștigată cu versiunile inițiale a evidențiat îmbunătățirile și modificările ce au permis creșterea utilității si aplicabilității standardului într-un număr tot mai mare de aplicații distribuite. Așa stau lucrurile, de exemplu, cu POA (Portable Object Adaptor), adaptorul portabil de obiecte. Funcțiile acestuia se referă la: crearea obiectelor și referințelor CORBA, dirijarea invocărilor de servicii către obiectele corespunzătoare, activarea si deactivarea obiectelor, etc. O alta noutate cu care vine CORBA 3.0 este referitoare la mesajele asincrone. Invocările de servicii de natură sincronă din versiunile inițiale sunt păstrate acum în CORBA Messaging Specification, dar sunt suplimentate de metodele de invocări asincrone, callback si polling. O alta îmbunătățire majora este transmiterea obiectelor prin valoare.

DCOM (Distributed Component Object) este soluția Microsoft, asemănătoare cu CORBA, pentru platforme Windows. Acesta permite un sistem de transmitere a mesajelor; un model de comunicare între obiecte COM (Component Object Model), un model de document compus OLE (Object Linking and Embedding), cu servicii de comunicare între documente si gestiunea lor; ActiveX, pentru aplicatii Web.

Atât CORBA, cât si DCOM separă interfețele obiectelor de implementările lor. Diferența constă în faptul ca limbajul de definire a interfețelor de la Microsoft diferă de CORBA IDL. Moștenirii obiectelor din CORBA i se opun mecanismele de agregare si delegare DCOM pentru reutilizarea obiectelor. În agregare obiectul exterior expune interfețele obiectelor interioare ca și cum ar fi ale sale și în delegare obiectul exterior retransmite delegările de metode către obiectele interioare. Practic, o interfață DCOM poate fi văzută ca o interfață de funcții, clientului dându-i-se un pointer pentru a avea acces la respectivele funcții, interfețele DCOM neputând fi instanțiate si neavând stări.

DCE (Distributed Computing Environment) este promovat de către OSF (Open Software Foundation). Facilități oferite: thread-uri, apeluri de procedură la distanță, servicii de directoare. Există un standard gateway între DCE și CORBA prin care CORBA poate lucra peste DCE (protocolul DCE CIOP). Diferența dintre DCE și CORBA constă în stilurile de programare adoptate: CORBA folosește un model obiectual, DCE are la baza un model procedural în care se folosesc apeluri la distanță (RPC – Remote Precedure Call).

Java, limbajul revoluționar al anilor '90 prin independența sa de platformă si totala orientare obiect nu prevedea la început servicii distribuite, obiectele Java de pe un server trebuind sa fie transferate la client pentru execuție. Neajunsul acelui sistem consta în faptul că obiectele își pierdeau informația pe perioada de inactivitate.

În Java există trei metode de abordare a comunicației între obiecte Java:

a) prin socket-uri;

Metoda este destul de flexibilă, dar este primitivă, implicând o abordare la nivel de protocol de comunicație.

b) prin RPC;

Comunicarea se face la nivel de procedură, evitându-se lucrul direct cu socket-urile. Pentru a apela proceduri prin RPC argumentele de intrare și valorile rezultate trebuie să respecte anumite reprezentări standard.

Modelul RPC

  Fig 1. Modelul RPC

Acțiunile numerotate în figură sunt următoarele:

(1) Procesul client apelează stub-ul client.

(2) Stub-ul client împachetează parametrii apelului într-un mesaj pe care pe care îl trimite kernel-ului mașinii client (serializare).

(3) Kernelul client trimite mesajul kernelului mașinii server.

(4) Kernelul server primește mesajul și îl trimite skeletonului de pe server.

(5) Skeletonul despachetează parametrii din mesaj și apelează procedura dorită a serverului (deseializare).

(6) Procedura este executată și rezulatul este întors către skeleton.

(7) Skeletonul împachetează rezultatul într-un mesaj și face apel la kernelul mașinii server.

(8) Kernelul server trimite mesajul înapoi la mașina client.

(9) Kernelul client preia mesajul si îl pasează stubului client.

(10) Stubul client despachetează rezultatul din mesaj si îl returnează procesului client (deserializare).

c) prin Java RMI;

Interfața de programare Java RMI se mulează perfect pe modelul orientat obiect oferit de Java, unde se pot crea obiecte ale căror metode pot fi invocate din alte mașini virtuale. Singura restricție este ca obiectele ce vor fi apelate de la distanță de către clienți sa fie instanțiate dintr-o clasa serialializabilă. O clasa este serializabilă daca orice instanță a sa poate fi transformată într-un șir de octeți (șirul de octeți putând fi salvat într-un fișier sau transmis către alta mașină virtuala Java) si restaurat. În Java o clasa este serializabilă dacă implementează una din interfețele: "java.io.Serializable", "java.io.Externalizable".

Relația între Java RMI și CORBA este mai mult una de complementaritate decât de concurență, Java fiind un limbaj ideal pentru a descrie obiecte CORBA. Facilitățile de cod mobil din Java permit împărțirea unei aplicații la momentul execuției pe două niveluri: client și server. Pe de alta parte, CORBA extinde Java cu un set de obiecte distribuite așa încât, de exemplu, un aplet poate comunica cu orice obiect indiferent de limbajul în care este scris și de localizarea în rețea. Alăturarea dintre Java și CORBA implică o serie de avantaje majore, Java intrând în funcțiune acolo unde CORBA se termină (obiecte Java implementează interfețe definite prin CORBA IDL). Java completează serviciile CORBA prin serviciile sale de colectare a gunoiului, controlând astfel ciclul de viață al obiectelor. Având în vedere toate cele spuse mai înainte se poate vorbi de o adevărată rivalitate între Java/CORBA pe de o parte și DCOM de cealaltă parte.

Funcționarea mecanismelor Java RMI și CORBA se bazează pe faptul ca pe mașina client exista un așa-numit client stub, o interfață între programul client si kernel-ul mașinii pe care lucrează, iar pe mașina server există un așa-numit server skeleton, care are cam aceleași funcții ca și stub-ul client. Acest mecanism este moștenit din RPC. Avantajul principal al folosirii obiectelor CORBA este dat de independența de limbaj, programele vechi care ofereau sau utilizau servicii nu trebuie să fie rescrise pentru a fi refolosite, ci este suficient sa fie configurate ca obiecte CORBA, de exemplu, un obiect C++ utilizat local de alte obiecte care apelau metodele sale poate fi exploatat de la distanță și de către un program Java, folosind CORBA. În această situație obiectul C++ care joacă rolul de server nu trebuie rescris, ci este suficientă scrierea unei interfețe pentru acesta prin care să se specifice ce serviciu oferă.

Independența de limbaj este posibilă datorită construcției acestei interfețe folosind limbajul IDL (Interface Definition Language). IDL permite ca toate obiectele CORBA să fie descrise în aceeași manieră, cerându-se în mod evident, și o legătură între limbajul nativ (C++, Java, etc.) si IDL. Legătura este realizată prin compilatoare care mapează (transformă) codul IDL în cod nativ. Obiectele CORBA comunică între ele cu ajutorul nucleului ORB (Object Request Broker) ca un intermediar. Nucleele ORB de la furnizori diferiți pot comunica peste TCP/IP folosind IIOP (Internet Inter-Orb Protocol). Exista multe nuclee ORB disponibile pentru diferite limbaje ca: Java, C++, SmallTalk, etc.

2. Standardul CORBA

2.1 Modelul de referință CORBA

Arhitectura fundamentală a standardului CORBA conține patru categorii de obiecte și o „magistrală” de legare a acestora. Fig. 2. prezintă această arhitectură[6].

Fig. 2. Arhitectura CORBA

2.1.1 Broker-ul de cereri de obiecte (ORB – Object Request Broker)

Este mecanismul de infrastructură standardizat de CORBA. Principalul său rol este acela de a uniformiza accesul la serviciile pentru aplicații. În acest scop el utilizează un mecanism de apel la distanță, orientat obiectual, pentru proceduri și metode. Structura ORB este detaliată în secțiunea următoare.

2.1.2 Serviciile de obiecte

Sunt interfețe standardizate care pun la dispoziția celorlalte obiecte din sistem o serie de operații și servicii fundamentale. Printre operații și servicii amintim: crearea obiectelor, asigurarea persistenței, controlul concurenței, securitatea, comportarea tranzacțională, tratarea evenimentelor etc. Trebuie remarcate două servicii de obiecte fundamentale[6]:

• Naming Service care permite clienților să găsească obiecte după numele acestora.

• Trading Service care permite clienților să găsească obiecte după proprietăți ale acestora.

2.1.3 Facilitățiile comune

Oferă interfețe standard pentru operații comune pentru grupe de aplicații. O astfel de facilitate comună se referă, spre exemplu, la componenta Distributed Document Component Facility. Ea, această componentă, permite schimbul de obiecte într-un document, cum ar fi legarea unei foi de calcul de un anumit raport, cele două fiind pe mașini diferite.

2.1.4 Interfețele de domenii

Reprezintă "standardele verticale" adoptate de OMG pentru diferite categorii (domenii) de aplicații: finanțe, medicină, telecomunicații, etc.

2.1.5 Interfețe de aplicații

Sunt cele create de programatori pe specificul fiecărei aplicații. Din perspectiva CORBA acestea sunt considerate ca interfețe nestandard.

2.2 Componentele CORBA

Figura 3. arata o cerere care este trimisă de la client la implementarea obiect[3]. Clientul este acea entitate care dorește să efectueze o operație pe obiect, iar implementarea obiect reprezintă codul și datele ce implementează obiectul.

Fig. 3. O cerere trimisa prin ORB

ORB pune la dispoziție toate mecanismele necesare pentru a găsi implementarea obiectului, pentru a o pregăti sa primească cererea și pentru transmiterea datelor ce alcătuiesc cererea. Interfața pe care clientul o vede este independentă de locația obiectului servant, de limbajul de programare în care este implementat, sau de orice alt aspect ce nu este reflectat în interfața obiectului.

Figura 4. arată structura individuală a unui ORB. Interfețele între componente sunt reprezentate ca și dreptunghiuri hașurate.

Interfețe identice pentru toate implementările ORB

Pot fi mai multe adaptore de obiecte

Sunt stub –uri și skeleton-uri pentru fiecare tip de lte adaptore de obiecte

Sunt stub –uri și skeleton-uri pentru fiecare tip de obiect

Interfața dependenta de ORB

Fig 4. Structura unu ORB

Principalele facilități și componente cu care operează CORBA sunt:

• nucleul ORB;

• limbajul de definire a interfețelor (IDL);

• depozit de interfețe;

• maparea (conversia) în limbaje de programare;

• componente stub și skeleton;

• apel dinamic și furnizare dinamică de obiecte;

• adaptori de obiecte;

• protocoale Inter-ORB.

2.2.1 Nucleul ORB

Obiectele CORBA comunică între ele cu ajutorul nucleului ORB (Object Request Broker) ca un intermediar [5]. Nucleele ORB de la furnizori diferiți pot comunica peste TCP/IP folosind IIOP (Internet Inter-Orb Protocol). Există multe nuclee ORB disponibile pentru diferite limbaje ca: Java, C++, SmallTalk, etc.

Funcții îndeplinite de un nucleu ORB:

oferirea de răspunsuri la cererile venite din partea unui client sau din partea unui alt ORB;

căutarea si instanțierea obiectelor de pe mașini aflate la distanță;

translatarea parametrilor dintr-un limbaj în altul;

invocarea de metode ale unor obiecte aflate la distanță;

invocarea statică sau dinamică a unor obiecte aflate la distanță.

2.2.2 Clienții

Un client al unui obiect are acces la o referință a obiectului și invocă operații pe respectivul obiect [7]. Un client cunoaște numai structura logică a obiectului, conformă cu interfețele sale și experiențele de comportament ale obiectului, prin invocări. De altfel se va considera, la modul general, că un client este un program sau un proces care inițiază cereri pe un obiect; este important să se poată face recunoașterea entităților care sunt client ale unui obiect dat. De exemplu implementarea unui obiect poate fi client pentru alte obiecte.

Clienții privesc, în general, obiectele și interfețele ORB prin perspectiva maparii limbajelor, aducând ORB-ul la nivelul programatorului. Clienții sunt portabili și trebuie să lucreze fără schimbări asupra sursei pe orice ORB care susține reprezentarea limbajului ceruta, cu orice instanță obiect care implementează interfața necesara. Clienții nu au cunoștințe despre implementarea obiectului, despre adaptorului obiect utilizat de implementare sau de natura ORB-ului utilizat pentru a-l accesa..

2.2.3 Implementarea obiect

Definește datele pentru instanța obiect si codul pentru metodele sale. Adesea se vor folosi și alte obiecte sau software adițional pentru implementarea comportamentului obiectului.

2.2.4 Depozitul de interfețe

Depozitul de interfețe este un serviciu care furnizează obiecte persistente care reprezintă informația IDL într-o forma disponibila pentru rulare [3]. Informația din depozitul de interfețe poate fi utilizată de ORB pentru a realiza cereri. Mai mult, utilizarea informației din depozitul de interfețe face posibil, ca un program ce întâlnește un obiect a cărei interfața nu este cunoscută la compilare să fie capabil să determine care sunt operațiile valide pe obiect și să facă invocări relativ la acestea.

Suplimentar rolului în functionarea ORB, depozitul interfețe este un spațiu comun pentru a stoca diferite informații asociate cu obiectele ORB. De exemplu, informațiile de corectare, bibliotecile de stub-uri sau skeleton-uri, rutine care lucrează pe tipuri particulare de obiecte pot fi asociate acestui depozit.

2.2.5 Apeluri dinamice

Exista o interfață care permite construcția dinamica de cereri pentru obiecte. În cazul interfețelor statice, clientul cheamă o rutina stub specifica pentru o anumita operație, pentru un anumit obiect [7]. In cazul apelului dinamic clientul specifica obiectul invocat, operația de executat, parametrii pentru operație, printr-un apel sau o secvență de apeluri. Codul client trebuie sa ofere informații despre operația de executat si tipurile parametrilor. Natura invocării dinamice poate varia substanțial de la maparea într-un limbaj de programare în altul.

2.2.6 Stub-uri idl

Pentru reprezentarea unui limbaj care nu este orientat obiect, trebuie să existe o interfață programabilă la stub-uri pentru fiecare tip de interfață [7]. La modul general, stub-urile vor prezenta accesul la operațiile definite OMG IDL relative la un obiect, de o manieră la care programarea devine naturală după cunoașterea OMG IDL și a reprezentării pentru limbajul de programare particular în discuție. Stub-urile realizează apeluri către restul ORB-ului prin interfețe care sunt private si presupus optimizate, pentru un nucleu ORB dat. În cazul în care este disponibil mai mult de un ORB, atunci trebuie sa fie stub-uri disponibile pentru fiecare dintre acestea. Pentru aceasta este necesar ca ORB –ul și procesul de mapare (transformare) în limbajul de implementare să coopereze pentru a asocia corect stub-urile cu referințele obiectelor.

2.2.7 Interfața ORB

Interfața ORB este interfața care se adresează direct ORB-ului, care este aceiași pentru toate ORB-urile și nu trebuie să fie dependentă de interfața obiect și adaptorul obiect. Din cauza că multe dintre funcționalitățile ORB sunt furnizate prin adaptorul obiect, stub-uri, structuri sau invocări dinamice, există puține operații care sa fie comune pentru toate obiectele [3]. Aceste operații sunt utile atât clienților cât și implementări de obiecte.

2.2.8 Skeleton-uri statice

Pentru o transformare (mapare) intr-un limbaj de implementare, si depinzând probabil de adaptorul de obiecte, trebuie sa existe o interfața la metodele care implementează fiecare tip de obiect [1]. Aceste metode sunt apelate de către ORB prin intermediul skeleton-ului.

Existența unui skeleton nu implică existența unui stub client corespunzător (clientul va putea face cereri prin intermediul interfeței de invocare dinamică).

Este posibilă scrierea unui adaptor de obiecte care să nu utilizeze skeleton-ul pentru a invoca metodele implementării. De exemplu, poate fi posibil sa se creeze dinamic implementări pentru limbaje cum ar fi Smalltalk.

2.2.9 Interfața pentru skeleton-urile dinamice

Este disponibila o interfață care permite manevrarea dinamica a invocărilor obiectelor. Aceasta înseamnă ca accesul la o operație specifică nu se face prin skeleton-ul care este specific respectivei operații, ci implementarea va fi atinsă printr-o interfață care permite accesul la numele și parametrii operației, similar cu mecanismul descris la apelurile dinamice.

2.2.10 Adaptorul de obiecte

Un adaptor de obiecte este calea primara prin care o implementare obiect accesează servicii furnizate [8] de un ORB. Este de așteptat să existe puține adaptoare obiect, care sa fie larg disponibile, cu interfețe adecvate pentru tipuri specifice de obiecte. Serviciile furnizate de un ORB prin adaptorul obiect includ, deseori, generarea și interpretarea referințelor obiect, invocare de metode, securitatea interacțiunilor, activarea si dezactivarea obiectelor, maparea referințelor obiect la implementări si înregistrarea implementărilor.

2.2.11 Referințele la obiecte

Este informația necesara pentru a specifica un obiect in cadrul ORB. CORBA specifică un format standard pentru referințele la obiecte, numit Interoperable Object Reference (IOR). În esență un IOR este un șir de identificare care identifică în mod unic și global obiectul. Similar cu URL, IOR precizează printre altele calculatorul unde se află obiectul, portul corespunzător și protocolul (interfața) pentru acces.

2.3 Limbajul de definire al interfețelor IDL

Transparența fața de limbajul de programare și fața de platforma pe care se execută este asigurată de un limbaj neutru, specializat numai pentru specificare: IDL (Interface Definition Language) [6] . Acesta este parte componentă a specificației CORBA. Toate operațiile pe care un client le poate cere unui obiect sunt descrise printr-o interfață IDL. Implementarea acestor interfețe este separată și depinde de implementarea concretă a specificațiilor CORBA.

IDL reprezintă o notație universal valabilă pentru descrierea unor interfețe pentru programarea aplicațiilor (API). „Programele” IDL sunt specificări pure, ele nu conțin nici un element de implementare.

Atunci când se face implementarea unor aplicații (realizarea componentelor server și client) va fi utilizat un anumit limbaj de programare, din care trebuie obținut accesul la elementele specificării IDL. In acest scop, ca parte a CORBA sunt definite și corespondențele (mappings) cu diverse limbaje de programare. Notația IDL este inspirată din sintaxa limbajului C++, fiind admise chiar și o serie de directive ale preprocesorului C++. Evident, cel mai agreat limbaj de map-are este C++, dar pe lângă acesta mai sunt și altele, Java fiind unul dintre ele.

2.3.1 Reguli de bază în IDL

În IDL identificatorii sunt case sensitive.

Doua nume din același spațiu (de exemplu: două interfețe din același modul, doua operații din aceeași interfață etc) nu se pot deosebi doar prin litere mari sau mici [2] . De exemplu intr-o aceeași interfață nu se admite definirea simultana a operațiilor anOperation și anOPERATION.

Comentariile sunt aceleași ca în Java și C++.

Toate definițiile din IDL sunt terminate cu semicolon (;). Definițiile care conțin alte definiții (precum module si interfețele) le încadrează pe acestea din urma cu paranteze ({}), ca și Java sau C++. Când o paranteză de închidere apare la sfârșitul unei definiții este urmată de semicolon.

2.3.2 Modulul IDL

Prima construcție a limbajului IDL examinată este modulul. Acesta este folosit pentru a grupa împreună definiții IDL ce împart același spațiu de nume [2] . Folosirea modulului este simplă. Declarația specifică numele modulului și încadrează membrii acestuia între acolade.

Exemplu:

module Bank {

interface Customer {…

};

interface Account {…

};

};

2.3.3 Tipuri primitive

Ca majoritatea limbajelor de programare IDL are o mare varietate de tipuri primitive care mai apoi pot fi combinate în tipuri agregate [2] .

void : tipul IDL void este analog tipului void din C/C++ și Java. Acest tip este folositor pentru metodele care nu returnează nici o valoare.

boolean: acest tip reține o valoare de adevăr. IDL definește două constante: true și false, a căror semnificație este evidentă. În funcție de limbajul de programare folosit aceste tipuri se vor mapa (transforma) la niște valori întregi (de exemplu C/C++) sau la tipul nativ boolean (cazul Java).

Exemplu: boolean aBoolean;

char și wchar: char în IDL este analog tipului char din C/C++ și Java, reține o singură valoare caracter. În aceste limbaje va fi transformat direct la tipul char corespunzător. Se reprezintă pe 8 biți. Tipul wchar este reprezentat pe 16 biți.

Exemplu: char aChar;

2.3.3.1 Tipuri reale

În IDL sunt definite câteva tipuri pentru a defini valorile reale.

float: se reprezintă pe 32 biți și este analog tipului float din C/C++, Java

Ex: float aFloat;

doble și longdouble: tipul double este analog tipului double din C/C++, Java și se reprezintă pe 64 de biți. Tipul longdouble reține numere în virgulă mobile extinse.

Ex: Double aDouble;

2.3.3.2 Tipuri întregi

În IDL sunt definite un număr de tipuri întregi. Spre deosebire de majoritatea limbajelor de programare în IDL nu este defint tipul int (integer) ci numai tipuri short sau long.

long și longlong: tipul long se reprezintă pe 32 biți, variază între -2^31 și 2^31-1 și este analog tipului C/C++ (pe majoritatea platformelor ) și tipul int din Java (pe toate platformele). Tipul longlong se reprezintă pe 64 biți.

Ex: long aLong;

short : se reprezintă pe 16 biți și este analog tipului short din C/C++ și Java. Conține valori între -216 și 215. Unsigned short conține valori între 0 … 216-1.

Ex: short aShort;

unsigned short anUnsignedShort;

2.3.3.3 Tipul octet

Se reprezintă pe 8 biți și nu se transformă în timpul transferului între două calculatoare. Nu are corespondență directă în C/C++, dar se folosește tipul char sau unsigned char pentru a reprezenta acest tip. În Java există tipul byte.

Ex: Octet anOctet;

2.3.3.4 Tipul string

Reprezintă un șir de caractere și este asemănător tipului Cstring din C/C++ și tipului String din Java. În C nu există o corespondentă directă și se folosesc șiruri de caractere. În IDL se pot defini atât stringuri cu lungime variabila cat si fixă.

Ex: string aFixedLengthString[20];

string aVariableLengthString;

2.3.3.5 Modificatorul const

În afară de aceste tipuri standard IDL permite, la fel ca si C/C++, definirea valorilor constante folosind modificatorul const.

Ex: const float aFloatConstant = 3.1415;

const long aLongConstant = 1234;

const string aStringConstant = „Ain’t IDL great ?”;

2.3.4 Tipuri construite

Tipurile construite care combină alte tipuri permit crearea tipurilor definite de utilizator [2] . Probabil cel mai folositor dintre aceste tipuri construite este interfața, care definește serviciile oferite de către obiecte.

2.3.4.1 Tipul enumerare

Tipul enumerare enum permite crearea unor tipuri ce dețin un set de valori predefinite specificate de enumerare. Deși identificatorii din enumerare sunt cuprinși într-o lista ordonată IDL nu specifică o ordine a lor. În C si C++ există un tip enumerare similar.

Ex: Enum DazOfWeek {

Sunday

Monday

Tuesday

Wednesday

Thursday

Friday

Saturday

};

2.3.4.2 Tipul structură

In IDL este definit un tip structură – struct – care conține, la fel ca și in C/c++, un număr de membri de tipuri diferite (chiar si alte struct). Acest tip este folositor în IDL deoarece, spre deosebire de obiectele CORBA (care sunt reprezentate de interfețe), este transmis prin valoare si nu prin referință. Cu alte cuvinte când un struct este transmis la un obiect îndepărtat, o copie a acelui struct este marshalizat spre obiect.

Ex: struct DateStruct {

short year,

short month,

short day,

short hour,

short minute,

short second,

long microsecond

};

2.3.4.3 Tipul union

Tipul union, ca și struct , reprezintă valori de tipuri definite. Este oarecum o combinație intre tipul union din C/C++ si o instrucțiune case.

Ex: union MultiplePersonalities switch(long) {

case 1:

short myShortPersonality;

case 2:

double myDoublePersonality;

case 3:

default:

string myStringPersonality;

};

În acest exemplu o variabilă de tipul MltiplePersonaliy poate avea ori o valoare short, ori double, ori string, în funcție de valoarea parametrului, atunci când union este folosit într-un apel de procedura .

2.3.4.4 Tipul interface

Interfața descrie serviciile oferite de un obiect CORBA. Aceste servicii apar sub forma unor metode și sunt similare metodelor din C/C++ și Java. Diferența este ca IDL este folosit doar pentru a specifica interfețele acestor metode.

Interfețele IDL sunt similare celor din Java pentru că nici unele nu descriu și implementări pentru metodele definite. Totuși o diferență este aceea că interfețele IDL pot conține atribute în timp ce cele din Java nu. In C++ nu există o corespondență directă cu toate că în aplicații se folosesc fișiere header pentru a defini interfața unei clase. Astfel o interfață IDL poate fi comparată cu un fișier header din C++, ce conține definiția unei clase. O clasa C++ ale cărei metode sunt virtuale poate fi considerată analoagă cu o interfață IDL.

Ca și clasele Java sau C++, o interfață IDL poate conține atribute și metode. Toate metodele definite intr-o interfață IDL sunt publice – pot fi apelate de către orice obiect ce deține o referință la implmentarea obiect a interfeței. De asemenea, deoarece interfețele descriu obiecte la distanță, IDL mai oferă în plus niște modificatori pentru a descrie interfața și membrii ei. Spre exemplu metodele pot fi declarate oneway, argumentele metodelor pot fi declarate in, out, inout; atributele pot fi declarate readonly.

Metode și parametrii

Metodele IDL pot folosi orice tip de data IDL ca și parametrii de intrare sau de ieșire – tipuri primitive, struct, sequence sau interfețe. Sintaxa generală pentru definirea unei metode este următoarea:

[oneway] return_type methodeName (param1_dir param1_type param1_name, …);

Modificatorul oneway este opțional; return_type specifică tipul de date întors de către metodă, param_dir specifică direcția fiecărui parametru (in,out,inout) si param_type specifică tipul fiecărui parametru.

Parametrii in sunt de intrare, transmiși prin valoare; out sunt parametrii de ieșire transmiși prin referință; inout sunt parametrii specificați prin referință care pot fi folosiți pentru transmiterea de valori în ambele sensuri.

Când un obiect apelează o metoda a unui obiect îndepărtat, așteaptă până când metoda se execută și întoarce rezultatul. Când metoda obiectului îndepărtat este declarată oneway obiectul care a apelat-o nu se va bloca ci își va continua execuția în timp ce metoda se va executa mai departe. Totuși această flexibilitate are un preț. Deoarece nu se așteaptă terminarea execuției metodei apelate, metoda nu poate returna o valoare si toți parametrii trebuie declarați in. Metodele oneway nu pot ridica excepții. Obiectul apelant nu are cum sa știe dacă metoda s-a executat cu succes, iar acesta nu este garantat.

Atributele

Un atribut IDL este analog cu unul din clasele Java sau C++, cu excepția că cele din IDL au întotdeauna vizibilitate publica. Sintaxa generală pentru definirea unui atribut este următoarea:

[readonly] attribute attribute_type attribute_name;

Deoarece se obișnuiește ca atributul să fie declarat private sau protected și să existe metode pentru a accesa valoarea atributului și pentru a o modifica, când un atribut va fi mapat(transformat) intr-un limbaj de programare se vor genera automat astfel de clase. De exemplu de la următoarea definiție:

attribute short myChanel;

se vor genera urmatoarele metodele:

short myChanel();

void myChanel(short myChanel);

Mai sus am menționat despre modificatorul readonly. După cum numele său sugerează acest modificator este folosit pentru a specifica ca un anumit atribut este doar pentru citire, valoarea sa nu poate fi modificată direct de către un obiect extern. Pentru un astfel de atribut compilatorul IDL va genera doar metoda de acces a valorii sale.

Moștenirea între interfețe

Interfețele IDL suporta noțiunea de moștenire. Astfel o interfață poate moșteni metodele și atributele unei alte interfețe. Sintaxa care specifică moștenirea între interfețe este asemănătoare cu sintaxa folosită în C++ și Java.

Ex: interface Fish {

};

interface halibut: Fish{

};

Interfețele IDL la fel ca și clasele C++, pot moșteni de la mai mult de o superclasă. Această capabilitate, cunoscută sub numele de moștenire multiplă, nu este posibilă în Java cu toate că Java permite ca o clasa să moșteneasca mai multe interfețe, o trăsătura care permite atingerea aceluiași rezultat ca și moștenirea multiplă.

Ex: interface LandVehicle{

};

interface WaterVehicle{

};

interface AmphibiousVehicle: LandVehicle, WaterVehicle{

};

Sintaxa pentru definirea unei interfețe este asemănătoare cu sintaxa folosită în C++ și Java.

Ex:

// Acest modul definește niște aparate electrocasnice.

module Appliances {

// Definiția interfeței Televizor.

interface Television {

// Numărul serial.

readonly attribute string mySerialNumber;

// Nivelul volumului.

attribute short myVolume;

// Canalul curent.

attribute short myChannel;

// Aprinde televizorul.

void turnOn();

// Stinge televizorul.

void turnOff();

// Setează timpul de stingere.

void setSleepTimer(in short minutes);

};

interface WWWTelevision : Television {

// Navighează la următorul URL.

void surfTo(in Internet::URL url);

};

};

2.3.5 Alte construcții IDL

2.3.5.1 typedef

Ca și in C/C++ IDL suportă declarația typedef permițând crearea numelor de tipuri definite de utilizator. Typedef poate face ca orice tip IDL să fie accesibil printr-un nume definit de utilizator.

Ex: typedef string SerialNumber;

2.3.5.2 Declarația anticipată

O declarație anticipată informează compilatorul IDL că definirea unui tip declarat va avea loc mai târziu. Sintaxa este foarte asemănătoare cu cea din C/C++.

Ex: module Circular {

// Forward declaration of the B interface.

interface B;

nterface A {

oid useB(in B aB);

};

nterface B {

void useA(in A anA);

};

};

2.3.6 Tipurile container

2.3.6.1 Tipul sequence

Este un tablou cu dimensiune variabilă. Valorile pot fi inserate sau scoase din listă dinamic. Toate valorile din tablou trebuie să fie de același tip.

Ex: sequence<float> tmperatureSequence;

2.3.6.1 Tipul tablou

Corespunde direct cu un tablou din C/C++ si Java. Are o lungime fixa.

Ex: string DayNames [7];

2.3.7 Exceptiile

CORBA și IDL suportă manipularea excepțiilor standard predefinite si a celor definite de utilizator [2] . Când se generează o excepție ea este transmisă cu ajutorul ORB-ului obiectului apelant.

2.3.7.1 Tipul exception

Este similar cu tipul struct și permite utilizatorului să definească excepții.

Ex:

//Aceasta excepție ar putea fi folosita atunci când se încearcă deschiderea unui

//fișier, iar fișierul nu este găsit.

exception FileNotFoundException{

string fileName //indica care fisier nu s-a gasit

};

//Aceasta excepție poate fi folosita când o operație care trebuie sa se termine

//intr-un anumit interval de timp nu se termina.

exception OperationTimedOutException {

string operationName;

longtimeoutLength;

};

//Cand incercarea de a se loga intr-un sistem esueaza.

//Nici o informatie suplimentara nu este necesara.

exception InvalidLoginException{

};

2.3.7.2 Exceptiile standard

CORBA are un număr de excepții predefinite, numite excepții standard. Acestea pot fi returnate de orice operație CORBA (in implementările de obiecte) sau direct de ORB. Cel puțin unele dintre excepțiile standard vor avea o semantică dependentă de limbajul de implementare a aplicațiilor sau de o anumită implementare ORB. Cele mai importante excepții standard sunt: UNKNOWN, BAD_PARAM, COMM_FAILURE, MARSHAL, NO_IMPLEMENT, BAD_OPERATION, OBJ_ADAPTER.

2.3.8 Tipul any

Tipul any poate reprezenta oricare dintre tipurile fundamentale sau tipurile construite.

Ex:

interface ObjectBrowser {

exception UnknownObjectType {

string message;

};

Void browseObject (in any object) raises (UnknownObjectType);

};

3. Dezvoltarea unei aplicații in CORBA

3.1 Etapele dezvoltării unei aplicații în CORBA

Pentru a dezvolta o aplicație CORBA sunt necesari următorii pași :

Definirea interfeței IDL

Definirea interfețelor se face folosind limbajul IDL [4] . Interfețele pot fi organizate in module, cate o interfață pentru fiecare tip de obiect servant.

Compilarea interfeței

Aceasta etapa presupune utilizarea unui compilator IDL – limbaj de implementare (de exemplu IDL – Java) pus la dispoziție de producătorii de ORB. In mod tipic un astfel de compilator furnizează ca rezultat următoarele tipuri de fișiere pentru fiecare interfața:

• stub-uri client pentru metodele definite în IDL, strict necesare pentru aplicații care utilizează apeluri statice;

• skeleton-uri pentru server – rolul acestora fiind comunicarea cu metodele server-ului și cu stub-urile din clienți;

Implementarea serverului

Odata ce a fost rulat compilatorul idlj, putem folosi skeleton-urile pe care le-a generat pentru construi aplicatia server. In afara de implementarea metodelor interfetei IDL serverul include si un mecanism pentru a porni ORB-ul si pentru a astepta invocarile de la client. Implemntarea Clientului

Clientul foloseste stub-urile generate de compilatorul idlj, porneste ORB-ul, gaseste serverul folosind serviciul de nume, obtine o referinta la obiectul indepartat si apeleaza metodele acestuia.

Rularea aplicatiei

O data implementate serverul si clientul, se porneste serviciul de nume, apoi serverul si apoi clientului

3.2 Definirea interfetei IDL si map-area ei in Java.

Pentru a crea fisierul Hello.idl :

Se creaza un director nou numit Hello

In acest director se deschide un fisier nou numit Hello.idl

In fisier se introduce urmator cod:

module HelloApp

{

interface Hello

{

string sayHello();

}

}

Se salveaza fisierul.

Sintaxa IDL pentru Hello.idl este extrem de simpla [4]. Se declara modulul CORBA IDL, interfata si operatiile, in cazul acesta una singura.

Un modul CORBA corespunde cu un pachet Java. O interfata in IDL corespunde cu o interfata Java.

Maparea fisierului Hello.idl in Java se face folosind compilatorul idlj. Acesta traduce (map-eaza) din IDL in Java. Comanda de compilare IDL este:

>idlj -fall Hello.idl

Optiunea -fall comanda generarea tuturor fisierelor posibile. Ca urmare, sunt generate urmatoarele fisiere, asupra carora nu trebuie intervenit:

1. Hello.java folosita de CORBA pentru signatura interfetei. Surprinzator, in acest fisier se specifica mostenire multipla, dar CORBA stie sa rezolve.

2. HelloOperations.java reprezinta translatarea efectiva a interfetei IDL in Java.

3. HelloPOA.java (Hello Portable Object Adapter) este pe post de skeleton la server. Aceasta clasa abstracta furnizeaza principalele functionalitati CORBA la nivel de server.

4. _HelloStub.java furnizează principalele functionalitati CORBA la nivel client.

5. HelloHelper.java furnizeaza o serie de functionalitati CORBA. Printre altele, aici se apeleaza metoda narrow si se fac citirile si scrierile cu stream-uri CORBA.

6. HelloHolder.java contine, printre altele, o instanța publica de tip Echo. De asemenea, de aici se deleagă HelloHelper spre a efectua operații I/O pe stream-uri CORBA.

3.3 Diagrama de clasa

HelloServer este clasa ce defineste serverul. HelloImpl este clasa ce implemnteaza interfata Hello. HelloClient este clasa ce defineste clientul.

Fig 5. Diagrama de clase pentru componeneta client

Fig 6. Diagrama de clase pentru componenta server.

3.4 Diagrama de colaborare

Fig 7. Diagrama de colaborare

3.5 Implementarea clasei servant si a serverului

Aplicația server constă din doua clase: clasa servant și clasa server. Servantul HelloImpl este implementarea interfeței IDL Hello. El este subclasă a clasei HelloPOA.

Servantul conține o metodă pentru fiecare operație IDL. Metodele sunt ca și metodele Java obișnuite, iar codul suplimentar pentru legătura cu ORB-ul, marsalizarea argumentelor și rezultatelor și așa mai departe este oferit de skeleton.

Clasa Server are metoda main() care :

Creează și inițializează o instanță ORB

Obține o referință la rădăcina POA și activează POAManager

Creează o instanță a obiectului servant și informează ORB despre existența ei

Obține o referință la contextul de nume în care va înregistra noul obiect CORBA

Obține rădăcina contextului de nume

Înregistrează noul obiect în contextul de nume sub numele “Hello”

Așteaptă invocări ale noului obiect din partea clienților

Sursa servantului este dată în programul următor:

// HelloImpl.java

import HelloApp.*;

public class HelloImpl extends HelloPOA {

// implementarea metodei sayHello()

public String sayHello() {

return "\nHello world !!\n";

}

}

Servantul este o subclasă a clasei HelloPOA, astfel moștenește funcționalitatea CORBA generată pentru ea de către compilator. Aici se declară și se implementează metoda sayHello( ) .

Sursa serverului este dată în continuare:

1. // HelloServer.java

import HelloApp.*;

import org.omg.CosNaming.*;

import org.omg.CosNaming.NamingContextPackage.*;

import org.omg.CORBA.*;

import org.omg.PortableServer.*;

import org.omg.PortableServer.POA;

import java.util.Properties;

public class HelloServer {

public static void main(String args[]) {

try{

// se creeză și initializează ORB-ul

ORB orb = ORB.init(args, null);

// se obține o referință la rădacina POA si se activează POAManager

POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

rootpoa.the_POAManager().activate();

// obiectul servant

HelloImpl helloImpl = new HelloImpl();

// se obține referința obiect către servant

org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl);

Hello href = HelloHelper.narrow(ref);

// se obtine referința contextului de nume

org.omg.CORBA.Object objRef =

orb.resolve_initial_references("NameService");

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

// se leagă referința obiect în Naming

String name = "Hello";

NameComponent path[] = ncRef.to_name( name );

ncRef.rebind(path, href);

System.out.println("HelloServer ready and waiting …");

// se așteaptă cereri de la clienți

orb.run();

}

catch (Exception e) {

System.err.println("ERROR: " + e);

e.printStackTrace(System.out);

}

}

}

Mai întâi se importă pachetele necesare:

// pachetul ce conține stub-urile pentru client

import HelloApp.*;

// pachet necesar pentru folosirea serviciului de nume

import org.omg.CosNaming.*;

// excepții speciale aruncate de către serviciul de nume

import org.omg.CosNaming.NamingContextPackage.*;

// toate aplicațiile CORBA au nevoie de clasele din acest pachet

import org.omg.CORBA.*;

// clase necesare pentru Portable Server

import org.omg.PortableServer.*;

import org.omg.PortableServer.POA;

// proprietăți pentru inițializarea ORB

import java.util.Properties;

Pentru că toate programele CORBA pot arunca excepțiii la rulare toată funcționalitatea funcției main a fost pusă într-un bloc try-catch. Excepții pot să apară în orice proces ce este implicat în invocare.

Un server CORBA are nevoie de un obiect local ORB, la fel ca și clientul CORBA. Fiecare server instanțiază un ORB și înregistrează obiectele servant astfel ca ORB-ul să le găsească atunci când primește o invocare pentru unul din ele. Variabila ORB este declarată și inițializată în interiorul blocului try-catch.

ORB orb = ORB.init(args,null);

Apelul către funcția init a ORB transmite argumentele din linia de comanda a serverului permițând astfel setarea unor proprietăți în timpul rulării.

ORB-ul obține referința inițiala la servicii cum ar fi serviciul de nume folosind metoda resolve_inițial_references

Referința la rădăcina POA este obținută și POAManagerul este activat din interiorul blocul try-catch

POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

rootpoa.the_POAManager().activate();

Operația activate schimbă() starea managerului POA în activ, obligând POA-urile asociate să înceapă procesarea cererilor. Managerul POA încapsulează starea de activare a POA-urilor cu care este asociat. Fiecare obiect POA are un obiect POAManager asociat. Un POA manager poate fi asociat cu unul sau mai multe obiecte POA.

Se instanțiază un obiect servant imediat după ce se activează managerul POA

HelloImpl helloImpl = new HelloImpl();

Următoarea linie de cod este folosită pentru a obține referința asociată obiectului servant. Metoda narrow( ) este necesară pentru a converti referințele obiect CORBA într-un tip specific.

org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl);

Hello href = HelloHelper.narrow(ref);

HelloServer lucrează cu serviciul de nume COS (Comon Object Service) pentru a face accesibile clientilor operațiile obiectelor servant. Serverul are nevoie de o referință la serviciul de nume pentru a putea înregistra referințele la diversele obiecte servant. Aceste referințe la obiecte sunt folosite de către clienți pentru a face apeluri de funcții.

Cele două opțiuni pentru naming service incluse in J2SE v.1.4 sunt:

orbd, care include atât un serviciu tranzitoriu cât și unul persistent

tnameserv un serviciu de nume tranzitoriu.

Acest exemplu foloseste serviciul tranzitoriu al orbd.

Se apelează metoda orb.resolve_initial_references() pentru a obține o referință la serviciul de nume.

org.omg.CORBA.Object objRef =

orb.resolve_initial_references("NameService");

Stringul “NameService” este definit pentru toate ORB-urile CORBA. ORB returnează o referință la contextul inițial de nume.

Ca toate referințele la obiecte CORBA, obj este un obiect CORBA generic. Pentru a fi folosit ca și un obiect NamingContextExt el trebuie convertit la acest tip. Acest lucru se face prin apelul funcției narrow() :

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

Obiectul ncRef este acum de tipul org.omg.CosNaming.NamingContextExt și poate fi folosit pentru a accesa serviciul de nume și a înregistra obiectele servant

Înregistrarea obiectului se face prin intermediul unui tablou cu obiecte de tip NameComponent. Deoarece calea către Hello are un singur element, se creează un tablou cu un singur element :

String name = "Hello";

NameComponent path[] = ncRef.to_name( name );

În final se transmite calea și referința către obiectul servant la serviciul de nume, legând referința de id-ul “Hello” :

ncRef.rebind(path, href);

Acum, când clientul va apela metoda resolve(“Hello”) a contextului de nume, serviciul de nume va returna o referință spre obiectul servant.

După un mesaj de așteptare afișat pe ieșirea standard a server-ului, se lansează thread-ul de așteptare după cereri de la clienți, prin apelul

orb.run();

3.6 Implementarea clasei client

Clientul este o plicație simplă, sursa sa este data in continuare [4] :

1. import HelloApp.*;

2. import org.omg.CosNaming.*;

3. import org.omg.CosNaming.NamingContextPackage.*;

4. import org.omg.CORBA.*;

5.

6. public class HelloClient

7. {

8. static Hello helloImpl;

9.

10. public static void main(String args[])

11. {

12. try{

13. // se crează și inițializează ORB-ul

14. ORB orb = ORB.init(args, null);

15.

16. // se obține rădăcina contextului de nume

17. org.omg.CORBA.Object objRef =

orb.resolve_initial_references("NameService");

19. // se folosește NamingContextExt în loc de NamingContext

NamingContextExt ncRef =

NamingContextExtHelper.narrow(objRef);

21.

22. // se obține referința la obiectul servant

23. String name = "Hello";

helloImpl =

HelloHelper.narrow(ncRef.resolve_str(name));

25. System.out.println("Obtained a handle on server object: " + helloImpl);

26. System.out.println(helloImpl.sayHello());

27. } catch (Exception e) {

28. System.out.println("ERROR : " + e) ;

29. e.printStackTrace(System.out);

30. }

31. }

32.}

Un client CORBA are nevoie de un obiect local ORB pentru a se ocupa de marsalizarea datelor și comunicare . Prima acțiune a clientului este de a instanția si inițializa un obiect ORB.

După identificarea serviciului de nume, se obține o referință ncRef la acest serviciu, prin metoda resolve_initial_references a obiectului orb, urmată de metoda narrow a clasei NamingContextExtHelper [6].

La randul ei, prin metoda resolve_str a acestei referințe compusă cu metoda narrow a clasei HelloHelper se obține referința la obiectul servant solicitat.

In final clientul afiseaza stringul returnat de metoda sayHello() a obiectului servant.

3.7 Punerea in lucru

Punerea în lucru are loc în felul următor [4,6]:

Se lanseaza pe masina serviciului de nume, serviciul de nume ( in cazul nostru orbd ):

>orbd -ORBInitialPort 1050 -ORBInitialHost localhost

Se compilează fișierul Heloo.idl folosind comanda

>idlj –fall Hello.idl

Se copiază unele dintre fișierele sursă Java generate pe mașina și în zona client, respectiv pe mașina și în zona server, după cum urmează:

În zona client sunt copiate fișierele următoare:

HelloClient.java

_HelloStub.java

Hello.java

HelloHelper.java

HelloOperation.java

În zona server sunt copiate toate fișierele generate:

HelloServer.java

HelloImpl.java

HelloOperation.java

HelloPOA.java

Hello.java

HelloHelper.java

HelloHolder.java

_HelloStub.java

Se lanseaza, pe mașina server, aplicația HelloServer, în care se specifică mașina serviciului de nume și portul la care așteaptă acesta:

>java HelloServer -ORBInitialPort 1050 -ORBInitialHost localhost

Se lansează pe mașina client aplicația HelloClient, în care se specifică mașina pe care se află serviciul de nume și portul la care ascultă acest serviciu.

>java HelloClient ORBInitialPort 1050 -ORBInitialHost localhost

3.8 Implementarea componentei server HelloServer în C++

Pentru a ilustra comunicarea între componente implementate în limbaje diferite, am ales sa implementez serverul HelloServer în C++.

Mai întâi pe mașina server trebuie instalat pachetul omniORB. Acest ORB este un software free și are implementate facilitățile CORBA obișnuite. Pentru instalarea și configurarea omniORB se poate consulta articololul [6].

3.8.1 Interfața și map-area în C++

Se va folosi aceeași interfața idl Hello.idl. Pentru compilarea ei se va folosi compilatorul omniidl furnizat de către distribuția omniORB. Comanda pentru compilare este următoarea:

omniidl –bcxx Hello.idl

Prin opțiunea -bcxx se specifică că rezultatul compilării va fi cod C++.

In urma compilării se generează doua fișiere:

Hello.hh pe post de header

HelloSK.cc pe post de sursă stub

Pentru a se putea lucra în Visual C++ numele primului fișier trebuie schimbat în Hello.h; numele celui de al doilea fișier trebuie schimbat în HelloSK.cpp; iar la începutul fișierului HelloSK.cpp se vor face următoarele modificări: în loc de #include “Hello.hh” se va pune #include “Hello.h”.

Aceste fișiere se vor folosi atât pe partea de server cat și pe partea de client.

In continuare pentru a se putea lucra in mediul Visual C++6.0 acesta va trebui configurat. Amănuntele configurării se pot găsi în articolul [6].

3.8.2 Implementarea servantului si a serverului

Pentru a simplifica lucrul în Visual C++6.0 am inclus în același text sursă atât implementarea servantului cât și a serverului. Sursa fișierului HelloServer.cpp este dată în continuare:

#include <iostream>

#include <string.h>

#include <Windows.h>

#include "Hello.h"

#define NUME "EchoomniORB"

using namespace std;

// Clasa servant

class HelloImpl : public POA_Hello,

public PortableServer::RefCountServantBase {

private:

int numar_apelatori;

public:

//implementarea metodei sayHello()

char* sayHello() {

char t[250];

sprintf(t, "%s",”Hello World !!”);

return CORBA::string_dup(t);

} // HelloImpl.sayHello()

}; // HelloImpl

int main(int argc, char** argv) {

// Creează și inițializează un ORB

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB4");

// Obține o referință la RootPOA & activează POAManagerul

CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");

PortableServer::POA_var poa = PortableServer::POA::_narrow(obj);

poa->the_POAManager()->activate();

// Instanțiază un obiect servant și obține o referință la el

HelloImpl *helloImpl = new HelloImpl();

poa->activate_object(helloImpl);

// Obține o referință corba la servant

obj = helloImpl->_this();

CORBA::Object_var orbobj;

CosNaming::NamingContext_var rootContext;

try {

// Localizează contextul root al serviciului de nume

orbobj = orb->resolve_initial_references("NameService");

rootContext = CosNaming::NamingContext::_narrow(orbobj);

// Construiește obiectul Name în vederea înregistrării servantului

CosNaming::Name objname;

objname.length(1);

objname[0].id = NUME;

objname[0].kind = (const char*)"Object";

// Înregistrează obiectul servant

rootContext->rebind(objname,obj);

} // try

catch (…) { // Exceptie universala

cerr << "Exceptie sistem!\n";

} // catch

helloImpl->_remove_ref(); // Șterge referința, nu obiectul servant!

cout << NUME << " wait …\n"; // Afiseaza mesaj de start

orb->run(); // Așteaptă cereri de la clienți

return 0;

}//main

Sursa începe cu implmentarea obiectului servant și a metodei sayHello().

In cadrul metodei main() sunt descrise toate acțiunile efectuate de către server. Acestea sunt aceleași cu acțiunile descrise în sursa HelloServer.java, doar că aici sunt descrise în C++. Ambele implementări respectă specificațiile CORBA în ceea ce privește serverul.

Se creează un obiect ORB inițializat cu ajutorul metodei init. Ca și parametrii aceasta primește argumentele liniei de comanda și un string care spune ca este vorba de o implementare omniORB. Se obține referința la rădăcina POA și se activează POAManagerul. Se creează o instanța a obiectului servant și se obține o referință la el. Se obține în continuare o referință la serviciul de nume.

Urmează construcția numelui de obiect și înregistrarea lui la serviciul de nume. Se pregătește un tablou cu elemente de tip Name. Aici fiind vorba de un singur obiect servant tabloul va avea o singura intrare. Fiecare intrare este o pereche:

id – string care conține numele efectiv al obiectului. Aici acest nume este HelloomniORB, specificat prin constanta NUME.

kind – string pe post de tip al obiectului. Implementarea omniORB pretinde ca acest tip sa fie Object. Astfel la acest servant va fi referit prin HelloomniORB.Object

Cu ajutorul referinței la serviciul de nume, a metodei rebind, a numelui si a referinței la obiectul servant se înscrie servantul la serviciul de nume.

Prin apelul orb->run() servantul rămâne în așteptarea contactărilor de către clienți.

Pentru lansarea în execuție se procedează în felul următor:

pe mașina serviciului de nume se lansează serviciul de nume:

>>orbd –ORBInitialPort 2983

pe mașina server se lansează programul server

>>HelloServer.exe

pe mașina client se lansează programul server, cu precizarea ca numele sub care este înregistrat obiectul servant la serviciul de nume este HelloomniORB.Object

>>java HelloClient –ORBInitialPort 2983 –ORBInitialHost localhost

4 Aplicația practica

4.1 Cerințe

O aplicație distribuită de prelucrare a imaginilor. Aplicația permite aplicarea unor filtre asupra unei imagini. Utilizatorul deschide o imagine, care este afișată într-o fereastră și are la dispoziție mai multe filtre pe care le poate aplica. De asemenea poate salva imaginea pe disc. Aplicația este formata din trei componente: client server si nod. Clientul preia informația ce compune imaginea și o trimite la server. Serverul împarte informația și o trimite mai multor noduri. Nodul este componenta care face prelucrarea efectiva a imaginii.

Componenta Client are o interfața grafică care permite utilizatorului să deschidă, să salveze o imagine și să aplice un filtru pe imaginea deschisă. De asemenea componenta client mai executa următoarele acțiuni:

Stabilește legătura cu server-ul și primește de la acesta un id asociat

Când utilizatorul dorește să aplice un filtru, informația (șirul de pixeli ce compun imaginea, tipul operației și id-ul clientului) este preluată și trimisă la server.

Primește informații (șir de pixeli) de la server și reactualizează imaginea.

Stabilirea legăturii cu serverul se face automat la lansarea aplicației.

Componenta Server înregistrează un client căruia îi atribuie un id. Înregistrează nodurile care vor prelucra imaginea. Primește informația de la client. Șirul de pixeli, ce compun imaginea, este împărțit in funcție de câte noduri sunt înregistrate la server, apoi fiecare subșir este trimis unui nod. Primește de la noduri șirurile de pixeli pe care le trimite la client.

Componenta Nod se ocupa de prelucrarea efectivă a imaginii. Primește de la server șirul de pixeli ce compun imaginea împreună cu tipul filtrului si prelucrează acest șir. Trimite server-ului noul șir.

4.2 Specificarea și analiza orientată obiect

4.2.1 Tabelul cerințelor

Un utilizator poate deschide un fișier imagine

Poate aplica mai multe filtre pe imagine

Poate salva imaginea pe disc

4.2.2 Diagrama cazurilor de utilizare:

Fig. 8.

Aplicarea filtrului

Actor: Utilizator

Scop: Prelucrarea unei imagini

Precondiții: fișierul ce se dorește a fi prelucrat să fie deschis, iar conținutul sa fie afișat pe ecran

Postcondițtii: imaginea este reactualizată

Desfășurare normală

Excepții:

Excepție la prelucrarea imaginii – se afișează mesaj de eroare și se revine la pasul 1

4.2.3 Diagramele de clase

Fig. 9 Diagrama claselor componentei Client

Fig. 10 Diagrama componentei Server

Fig. 11 Diagrama componentei Nod

Cardurile CRC sunt descrise in anexa A.

4.2.4 Arhitectura

Aplicația este formată din trei componente care comunică între ele utilizând tehnologia CORBA. Comunicarea se realizează prin protocolul IIOP ( Internet Inter-ORB Protocol ), care este varianta specializată a protocolului GIOP ( General Inter-ORB Protocol) pentru TCP/IP. Cele trei componente sunt Client, Server și Nod.

Fig. 12 Arhitectura aplicatiei

Pot exista mai multe componente client si nod care sunt conectate in acelasi timp la server.

Client: Componenta cu care lucrează utilizatorul. Conține o interfață grafică in care este deschisă imaginea. La apelul unui filtru aceasta trimite componentei server un șir de pixeli ce compun imaginea.

Server: Componenta care se ocupă cu gestionarea clienților și a nodurilor. Primește șirul de pixeli ce compun o imagine de la un client și după un anumit algoritm îl împarte în subșiruri, pe care le trimite la noduri. De la noduri primește subșirurile de pixeli pe care le trimite înapoi la clientul corespunzător.

Nod: Componenta care prelucrează o imagine. Primește un șir de pixeli de la Server pe care aplică operațiile ce definesc un anumit filtru. Trimite înapoi la server șirul de pixeli.

4.2.4.1 Diagrama de componente

Fig. 13 Diagrama de componente

4.2.4.2 Împărțirea pe pachete

Fig. 14 Împărțirea pe pachete

4.3 Proiectarea (Orientată Obiect)

4.3.1 Diagramele de secvență

Prima acțiune a aplicației este: componentele client si nod se înregistrează la server. Acest lucru se produce la lansarea în execuție a aplicației. Următoarea diagrama de secvența ilustrează aceasta acțiune.

Fig. 15 Înregistrarea componentelor Nod și Client la Server

Aceasta diagrama de secvența descrie cazul de utilizare aplicarea filtrului.

Fig. 15 Aplicarea unui filtru

4.3.2 Diagrama de colaborare

Fig. 17 Diagrama de colaborare

Diagramele detaliate ale claselor sunt descrise in Anexa 2.

4.4 Manual utilizator

Aplicația este compusă din trei componente. Pentru o execuție optimă a aplicației este recomandat ca fiecare componentă să ruleze pe câte o mașina. Mai întâi se lansează în execuție componenta Server, apoi componenta Nod și apoi Client.

Pentru a lansa în execuție componenta Server se procedează în felul următor:

Se lansează serviciul de nume pe mașina server. Comanda este următoarea:

orbd –ORBInitialPort 1050 –ORBInitialHost numele_masinii_server

In fișierul Config.txt se setează numărul de port și numele mașinii după cum urmează:

org.omg.CORBA.ORBInitialPort

1050

org.omg.CORBA.ORBInitialHost

numele_masinii_server

Se lansează în execuție componenta Server

java Server

Pentru a lansa în execuție componenta Nod se procedează în felul următor:

În fișierul config.txt se setează numărul de port și numele mașinii server ca mai sus

Se lansează în execuție componenta Nod:

java Nod

Pentru a lansa în execuție componenta Client se procedează ca la Nod:

Se setează informațiile în fișierul config.txt ca în cazul Nod-ului

Se lansează în execuție componenta Client

java Client

Interfața Client arata în felul următor:

Fig. 18 Interfața client

Interfața este compusă dintr-un meniu și o zona în care se poate afișa conținutul fișierului imagine.

Meniul este compus din submeniurile: File și Operations.

Pentru a deschide o imagine se folosește opțiunea din meniu File->Open. Se va deschide o fereastra dialog din care se poate alege imaginea.

Fig. 19 Deschiderea unei imagini

Pentru a salva o imagine deschisă se folosește opțiunea File->Save. Se va deschide, ca și în cazul anterior o fereastra dialog ce permite salvarea fișierului.

Optiunea File->Exit închide aplicația Client.

Pentru a plica un filtru se folosește una din opțiunile meniului Operations.

Operations->GaussianNoise

Operations->PoissonNoise

„noie” = efectul obținut atunci când pixelii din aceeași regiune de culoare au culori diferite. „Noise” apare adesea când o imagine este redimensionată sau convertită la un alt format.

Primele doua filtre adaugă zgomot(noise) unei imagini după doua formule diferite.

Operations->Red : Schimbă intensitatea culorii roșii a fiecărui pixel

Operations->Green : Schimbă intensitatea culorii verzi a fiecărui pixel

Operations->Contrast : Modifică contrastul imaginii.

Similar Posts