Grup de Discutii pe Internet
1. Introducere
Numele aplicatiei – “Grup de discutii pe Internet” – sugereaza faptul ca aceasta tema de proiect isi propune sa ofere o varianta de implementare pentru ideea de schimb de date in timp real, avind drept suport reteaua Internet.
Multe categorii de utilizatori – care folosesc calculatorul acasa, la scoala, in firme mici sau mari – pot acum sa beneficieze din plin de intinderea retelei Internet sau de retelele interne ale firmelor pentru a comunica si a colabora mai eficient in timp real.
Grupurile de interese de pe tot globul, ce poarta discutii privind orice subiect imaginabil, fac parte din realitatea cotidiana a unei anumite categorii de persoane, iar aceasta categorie va creste pina la dimensiunile intregii omeniri. Discutiile, in care o persoana trimite un mesaj si toti ceilalti abonati ai grupului de interes pot sa-l citeasca, se deruleaza in toate stilurile posibile. Atunci cind o anumita persoana considera ca poate beneficia de experienta altor persoane, intr-un anumit domeniu, se poate interesa daca exista un grup deja format in care se discuta probleme avind ca teme subiecte din sfera sa de interese. Aceasta este o alternativa viabila a solutiei care ar insemna browsing-ul pe Internet, in cautarea informatiei.
Grupurile de discutii au aparut inca de la inceputurile retelei Internet, permitind oamenilor de stiinta, interesati de acelasi subiect, sa emita intrebari si sa primeasca raspunsuri. Astazi, aceste grupuri pot fi asemanate cu cu cele care se formeaza intr-o locuinta sau local public, cu exceptia faptului ca informatiile se transmit prin scris (desenat), si nu prin viu grai. Fiecare participant poate citi ceea ce au scris altii si poate face propriile comentarii.
Exista mii de grupuri de discutii, ale caror subiecte acopera domenii ca stiinte sociale, calculatoare, literatura, economie, hobby-uri, economie etc. In cadrul acestor grupuri se pot face oferte de lucru, contracte, propuneri de afaceri, anunturi ale unor evenimente, se pot face schimburi de materiale in format electronic. Aceste facilitati sint obtinute fara a fi necesara parasirea mesei de lucru. Pe de alta parte, comunicarea se poate realiza la momentul ales de initiator.
Astazi, multe site-uri web au grupuri de discutii inglobate. Programe specializate ofera posibilitatea conectarii la un anume grup de discutii, in functie de preferintele solicitantului.
Se doreste realizarea unui un program care sa permita unui grup de discutii sa schimbe atat mesaje, lucru obisnuit in cazul grupurilor de discutii, cat si imagini si continut executabil.
Initiatorul grupului de discutii va trebui sa furnizeze informatii cu privire la momentul ales pentru declansarea discutiilor si sa puna la dispozitia celor interesati softul necesar participarii. In momentul in care un participant se conecteaza, initiatorul grupului de discutii trebuie sa fie, deja, in asteptarea cererilor, pentru a putea inregistra clientii. Dupa ce se inregistreaza la grupul de discutii, un participant poate lua parte, in mod efectiv, la conversatie.
Toti participantii au aceleasi drepturi. Cu toate acestea, managerul poate interveni pentru a opri din actiune un anume client care nu respecta regulile grupului de discutii.
2. Arhitectura aplicatiei
Aplicatia se executa pe un sistem distribuit format din mai multe masini, dintre care una joaca rol de server iar celelalte de clienti. Stabilirea serverului se face la pornirea sesiunii (masina de pe care se initiaza grupul de discutii va juca rolul de server pe parcursul existentei grupului respectiv).
Schematic, sistemul se prezinta astfel:
Fig. 1 – Schema grupului de discutii
Aplicatia este formata din mai multe module:
modul server care are rol de coordonare si decizie in cadrul sistemului.;
module clienti care se executa pe fiecare masina client.
Modulele client sunt constituite din trei sectiuni logice, fiecare tratind un alt aspect al comunicatiei. Cele trei sectiuni se ocupa, respectiv, de chat (discutii sub forma de text), transfer de informatie in format grafic si transfer de informatie sub forma unui continut executabil.
Arhitectura poate fi vazuta ca fiind de tipul client-server. Programele client ruleaza pe masini locale si solicita servicii, prin intermediul Internet-ului, serverului. Fiecare modul, dintre cele trei prezentate mai sus, are un corespondent pe masina client, cu care dialogheaza in vederea realizarii functionalitatii specifice. Pe masina server, programele reprezentind, primele doua module, ruleaza algoritmi ce au rolul de a implementa politici de alocare a drepturilor catre clienti. Clientii interactioneaza cu modulele locale prin intermediul unei interfete grafice, solicitind servicii catre server. Confirmarea primirii unui anume drept ii este adusa la cunostinta clientului prin intermediul modificarii unor controale grafice. Clientul poate decide pastrarea sau cedarea controlului intr-o masura limitata de catre server. Aceasta decizie este necesara pentru a pastra un anumit grad de echitate in ceea ce priveste alocarea drepturilor si durata acestei alocari.
Gestiunea clientlor conectati, la un moment dat, este tinuta de server intr-o baza de date.
La abandonarea discutiei, clientul anunta acest lucru server-ului. Din cind in cind, server-ul verifica daca un client cu care nu a mai comunicat un anume timp mai este activ. In caz ca nu primeste raspuns, clientul respectiv este scos din baza de date.
Server-ul primeste cereri de servicii de la clienti. In caz ca serviciul respectiv este disponibil, ele este oferit pe loc clientului. In caz contrar, clientul este inregistrat intr-o coada de asteptare, evoluind in cadrul acesteia in functie de o anumita politica. Clientul nu are posibilitatea sa mai faca o cerere pentru acelasi serviciu in caz ca cererea anterioara nu a fost, inca, acceptata. Are, insa, posibilitatea sa anunte ca renunta la cererea sa, fapt care se traduce, de partea server-ului, prin scoaterea clientului respectiv din coada de asteptare. Solicitarea unui client de a renunta la conexiunea cu server-ul duce, in mod automat, la scoaterea primului din toate cozile de asteptare asociate diverselor servicii solicitate anterior.
Clientii se pot decide asupra unuia sau mai multor servicii (dintre cele trei) de care vor sa beneficieze.
In ceea ce priveste alocarea drepturilor, aceasta se putea face si prin metoda votului, ceea ce ar fi dus la acordarea dreptului solicitat acelui participant care a obtinut cel mai mare numar de voturi din partea celorlalti. S-a renuntat la aceasta varianta deoarece, pe de-o parte, poate duce la inechitate, anumiti participanti putind fi favorizati de aprecierea unanima si, pe de alta parte, ar implica un schimb de mesaje suplimentar ce nu este binevenit, tinind seama de constringerile tehnice aparute in practica.
Ideea sincronizarii alocarii resurselor (detinerea simultana a dreptului de a desena si trimite text), ca modalitate de evolutie a sistemului, a fost abandonata, datorata unei anume lipse de naturalete si a overhead-ului implicat.
Sectiunea chat reprezinta modalitatea prin care se pot schimba informatii sub forma de text. Odata cu primirea dreptului de a scrie, afisat corespunzator in suprafata grafica respectiva, clientul respectiv poate introduce text de la tastatura. Acest text este preluat si transmis modulului server care il difuzeaza celorlalti participanti inregistrati pentru acest serviciu. Un client poate scrie pina cind decide, el insusi, sa intrerupa introducerea de text sau pina cind este intrerupt de catre server, in urma expirarii timpului acordat.
Sectiunea board are drept scop circulatia informatiilor de tip grafic. Aceasta se refera, concret, la desenarea, pe o suprafata special destinata acestui scop, utilizind mouse-ul. In cazul in care clientului i se preleveaza dreptul de a emite, in momentul in care mai exista informatie netransmisa in buffer-ul local, aceasta nu se va pierde. Ea va fi difuzata odata cu reprimirea dreptului de transmisie de catre client.
Sectiunea demo va fi destinata incarcarii, pe masina client, a unui fisier cu continut executabil, avind rolul de exemplificare sau subliniere a informatiilor difuzate prin celelalte doua cai. Acest fisier va putea fi furnizat de oricare dintre clienti, fara a necesita solicitarea managerului, prin intermediul indicarii unei adrese URL. Fisierul poate fi acceptat, sau nu, de catre clientul ce il receptioneaza. Vor exista restrictii in ceea ce priveste dimensiunea fisierului, tinind seama de considerente de trafic in retea.
Grupul de discutii este initiat de catre administratorul masinii server. Modulul server nu trebuie sa fie, neaparat, instalat pe masina initiatorului. Pe de alta parte, toti participantii la grupul de discutii trebuie sa se afle in posesia pachetului de programe necesar participarii la discutii.
Pe masina coordonatorului discutiilor vor fi disponibile informatii cu privire la numarul de participanti la momentul respectiv si diferite statistici necesare administrarii sistemului. Anumite informatii vor fi aduse si la cunostinta clientilor.
3. Implementare
3.1 Alegerea limbajului
Practica actuala, de utilizare a Internet-ului ca mediu pentru difuzarea de informatii si programe conduce catre ideea existentei unei platforme comune pe care sa fie dezvoltate diversele aplicatii. Conceptul de a realiza aplicatii care sa aiba drept numitor comun platforma pe care ruleaza a capatat expresie practica prin aparitia limbajului Java. Costurile implementarii aplicatiilor pot scadea considerabil in cazul in care nu sint necesare adaptari pentru o anumita platforma hardware sau un anumit sistem de operare.
Platforma Java este un mod fundamental nou de a aborda calculul, bazat pe puterea retelelor si pe ideea ca acelasi software ar trebui sa ruleze pe diferite tipuri de calculatoare sau, pur si simplu, dispozitive.
Utilizind tehnologia Java, se poate rula aceeasi aplicatie pe orice tip de masina, PC, Macintosh, computer de retea si altele. Mai mult, componentele acestei tehnologii nu tin seama ca sint rulate pe un telefon, TV sau sistem de operare; trebuie doar sa existe capacitatea respectivei platforme de a suporta JVM (masina virtuala Java). Tehnologia Java este considerata revolutionara deoarece este proiectata sa permita calculatoarelor si diverselor alte dispozitive sa sa comunice intre ele mult mai usor decit pina acum.
O alta calitate a JVM este cea care permite scrierea de soft prin intermediul caruia sa se poata exploata numeroasele baze de date, construite in ultimele decenii, utilizind sisteme de calcul si software din generatiile actuale. Java ofera posibilitatea de a accesa respectivele informatii din Internet, intr-o maniera transparenta.
Ca exemple de utilizare a aplicatiilor scrise in Java se pot da tehnologiile software utilizate in marile companii pentru a monitoriza tranzactiile si a pastra consistenta datelor existente pe diferitele unitati de calcul din sistem. Alte companii utilizeaza tehnologia Java pe propriile site-uri web, pentru comunicare si asigurarea fluxului de informatii intre departamente, furnizori si clienti.
Retelele de calculatoare utilizeaza software ce trebuie sa indeplineasca anumite cerinte in ceea ce priveste portabilitatea, modularitatea si securitatea – zone in care Java este remarcabil, pentru ca a fost proiectat pentru a fi folosit pentru retele, initial.
Limbajul Java a fost proiectat pornindu-se de la C++, din care s-au eliminat citeva elemente care au fost considerate ca fiind generatoare de erori (aritmetica pointer-ilor, colectarea explicita a reziduurilor din memorie, mostenirea multipla s.a.).
Solutia de implementare pentru Java a fost aleasa pentru a permite utilizarea sa pentru programarea in internet. Ideea principala urmarita a fost “Write once, run anywhere”.
In informatica actuala, portabilitatea programelor nu poate fi realizata decit printr-o executie interpretativa. In mod traditional un in. In caz ca nu primeste raspuns, clientul respectiv este scos din baza de date.
Server-ul primeste cereri de servicii de la clienti. In caz ca serviciul respectiv este disponibil, ele este oferit pe loc clientului. In caz contrar, clientul este inregistrat intr-o coada de asteptare, evoluind in cadrul acesteia in functie de o anumita politica. Clientul nu are posibilitatea sa mai faca o cerere pentru acelasi serviciu in caz ca cererea anterioara nu a fost, inca, acceptata. Are, insa, posibilitatea sa anunte ca renunta la cererea sa, fapt care se traduce, de partea server-ului, prin scoaterea clientului respectiv din coada de asteptare. Solicitarea unui client de a renunta la conexiunea cu server-ul duce, in mod automat, la scoaterea primului din toate cozile de asteptare asociate diverselor servicii solicitate anterior.
Clientii se pot decide asupra unuia sau mai multor servicii (dintre cele trei) de care vor sa beneficieze.
In ceea ce priveste alocarea drepturilor, aceasta se putea face si prin metoda votului, ceea ce ar fi dus la acordarea dreptului solicitat acelui participant care a obtinut cel mai mare numar de voturi din partea celorlalti. S-a renuntat la aceasta varianta deoarece, pe de-o parte, poate duce la inechitate, anumiti participanti putind fi favorizati de aprecierea unanima si, pe de alta parte, ar implica un schimb de mesaje suplimentar ce nu este binevenit, tinind seama de constringerile tehnice aparute in practica.
Ideea sincronizarii alocarii resurselor (detinerea simultana a dreptului de a desena si trimite text), ca modalitate de evolutie a sistemului, a fost abandonata, datorata unei anume lipse de naturalete si a overhead-ului implicat.
Sectiunea chat reprezinta modalitatea prin care se pot schimba informatii sub forma de text. Odata cu primirea dreptului de a scrie, afisat corespunzator in suprafata grafica respectiva, clientul respectiv poate introduce text de la tastatura. Acest text este preluat si transmis modulului server care il difuzeaza celorlalti participanti inregistrati pentru acest serviciu. Un client poate scrie pina cind decide, el insusi, sa intrerupa introducerea de text sau pina cind este intrerupt de catre server, in urma expirarii timpului acordat.
Sectiunea board are drept scop circulatia informatiilor de tip grafic. Aceasta se refera, concret, la desenarea, pe o suprafata special destinata acestui scop, utilizind mouse-ul. In cazul in care clientului i se preleveaza dreptul de a emite, in momentul in care mai exista informatie netransmisa in buffer-ul local, aceasta nu se va pierde. Ea va fi difuzata odata cu reprimirea dreptului de transmisie de catre client.
Sectiunea demo va fi destinata incarcarii, pe masina client, a unui fisier cu continut executabil, avind rolul de exemplificare sau subliniere a informatiilor difuzate prin celelalte doua cai. Acest fisier va putea fi furnizat de oricare dintre clienti, fara a necesita solicitarea managerului, prin intermediul indicarii unei adrese URL. Fisierul poate fi acceptat, sau nu, de catre clientul ce il receptioneaza. Vor exista restrictii in ceea ce priveste dimensiunea fisierului, tinind seama de considerente de trafic in retea.
Grupul de discutii este initiat de catre administratorul masinii server. Modulul server nu trebuie sa fie, neaparat, instalat pe masina initiatorului. Pe de alta parte, toti participantii la grupul de discutii trebuie sa se afle in posesia pachetului de programe necesar participarii la discutii.
Pe masina coordonatorului discutiilor vor fi disponibile informatii cu privire la numarul de participanti la momentul respectiv si diferite statistici necesare administrarii sistemului. Anumite informatii vor fi aduse si la cunostinta clientilor.
3. Implementare
3.1 Alegerea limbajului
Practica actuala, de utilizare a Internet-ului ca mediu pentru difuzarea de informatii si programe conduce catre ideea existentei unei platforme comune pe care sa fie dezvoltate diversele aplicatii. Conceptul de a realiza aplicatii care sa aiba drept numitor comun platforma pe care ruleaza a capatat expresie practica prin aparitia limbajului Java. Costurile implementarii aplicatiilor pot scadea considerabil in cazul in care nu sint necesare adaptari pentru o anumita platforma hardware sau un anumit sistem de operare.
Platforma Java este un mod fundamental nou de a aborda calculul, bazat pe puterea retelelor si pe ideea ca acelasi software ar trebui sa ruleze pe diferite tipuri de calculatoare sau, pur si simplu, dispozitive.
Utilizind tehnologia Java, se poate rula aceeasi aplicatie pe orice tip de masina, PC, Macintosh, computer de retea si altele. Mai mult, componentele acestei tehnologii nu tin seama ca sint rulate pe un telefon, TV sau sistem de operare; trebuie doar sa existe capacitatea respectivei platforme de a suporta JVM (masina virtuala Java). Tehnologia Java este considerata revolutionara deoarece este proiectata sa permita calculatoarelor si diverselor alte dispozitive sa sa comunice intre ele mult mai usor decit pina acum.
O alta calitate a JVM este cea care permite scrierea de soft prin intermediul caruia sa se poata exploata numeroasele baze de date, construite in ultimele decenii, utilizind sisteme de calcul si software din generatiile actuale. Java ofera posibilitatea de a accesa respectivele informatii din Internet, intr-o maniera transparenta.
Ca exemple de utilizare a aplicatiilor scrise in Java se pot da tehnologiile software utilizate in marile companii pentru a monitoriza tranzactiile si a pastra consistenta datelor existente pe diferitele unitati de calcul din sistem. Alte companii utilizeaza tehnologia Java pe propriile site-uri web, pentru comunicare si asigurarea fluxului de informatii intre departamente, furnizori si clienti.
Retelele de calculatoare utilizeaza software ce trebuie sa indeplineasca anumite cerinte in ceea ce priveste portabilitatea, modularitatea si securitatea – zone in care Java este remarcabil, pentru ca a fost proiectat pentru a fi folosit pentru retele, initial.
Limbajul Java a fost proiectat pornindu-se de la C++, din care s-au eliminat citeva elemente care au fost considerate ca fiind generatoare de erori (aritmetica pointer-ilor, colectarea explicita a reziduurilor din memorie, mostenirea multipla s.a.).
Solutia de implementare pentru Java a fost aleasa pentru a permite utilizarea sa pentru programarea in internet. Ideea principala urmarita a fost “Write once, run anywhere”.
In informatica actuala, portabilitatea programelor nu poate fi realizata decit printr-o executie interpretativa. In mod traditional un interpretor este considerat lipsit de performanta. Pentru a se depasi acest handicap, codul binar rezultat in urma compilarii este destinat unei masini virtuale, orientate stiva (JVM). Codul astfel obtinut este interpretat de catre un interpretor. Trecerea pe o platforma noua implica existenta unui emulator pentru masina virtuala.
Portabilitatea programelor Java este asigurata si prin faptul ca tipurile de date fundamentale sint standardizate, si nu mai depind de calculator. In limbajele C si C++, tipurile fundamentale au dimensiuni diferite in functie de hardware si de sistemul de operare. Aceasta dependenta traditionala dintre reprezentarea datelor si tipul calculatorului este justificata de structura memoriei, care este specifica fiecarui tip de calculator. De asemenea, sistemul de operare poate adopta anumite conventii in legatura cu reprezentarea datelor (Windows – un intreg are 16/32 biti).
3.2 Caracteristici care au impus alegerea limbajului
Avind in vedere ca Java este un mediu de programare si utilizare distribuit pe Internet, se impun cele mai sigure metode de protectie, atit pentru sistemele clientilor, cit si pentru servere, fara sa se compromita prin aceasta ideea de sistem distribuit. Java permite crearea unor sisteme protejate, care sa nu poata fi virusate sau violate. Mijloacele prin care Java ofera acest inalt nivel de protectie sint:
Eliminarea pointerilor – actiunile preventive constituie intotdeauna prima linie de aparare impotriva pericolelor. Operind in acest spirit, proiectantii Java au eliminat cele mai eficiente mijloace prin care s-ar putea atenta la securitatea sistemelor, si anume pointerii. Acestia, impreuna cu aritmetica asociata lor sint, in acelasi timp, si cauza principala a distrugerii accidentale a sistemelor. De vina sint, in masuri apropiate, lipsa de experienta si lipsa de control. Cu pointeri se pot scrie programe care sint corecte sintactic si semantic dar pot cauza caderea sistemului;
Gestiunea automata a memoriei – gestiunea memoriei in C si C++ este facuta, in mod explicit, de catre programator, cu ajutorul instructiunilor malloc si free, la care se adauga mai multe functii standard pentru gestiunea memoriei, din biblioteci. Totusi, gestiunea corecta a memoriei este un lucru pretentios, iar o mare parte din erorile intilnite in programele de C si C++ provin tocmai din folosirea incorecta a functiilor de gestiune a memoriei. In Java, gestiunea memoriei este facuta automat, nu este lasata in seama programatorului. Cind este creat un obiect i se aloca automat memoria necesara, apoi este urmarit in tot timpul executiei prin referintele care se fac la el. Cind sistemul de executie vede ca nu se mai fac referinte la un obiect, plaseaza acel obiect in colectorul de reziduuri.
Alocarea dinamica a memoriei pentru clase – in Java este eliminata alocarea statica a memoriei. Se stie ca in C si C++ o parte din alocarea memoriei este facuta in mod static, in timpul compilarii. In Java, alocarea memoriei pentru clase este facuta in mod dinamic, in timpul executiei. Datorita acestui fapt, un intrus care ar dori sa forteze accesul la o aplicatie, nu se poate baza pe o structura fixa a memoriei.
Spatiu separat pentru fiecare clasa – in timpul executiei, interpretorul Java ofera pentru fiecare clasa incarcata un spatiu separat pentru variabile. In acest fel,
se evita referirea accidentala a variabilelor sinonime care apartin altor clase.
Verificarea codului binar inainte de executie – prima faza a interpretorului Java este un verificator de cod binar. Cind un program binar Java este incarcat pentru a fi executat in mod interpretativ, inainte de a incepe executia programul este verificat daca este conform cu anumite conventii ale limbajului si ale mediului Java. Chiar si in cazul in care compilatorul genereaza cod corect, programul binar ar putea fi modificat, cu sau fara intentie, intre faza de compilare si faza de executie. Codul binar Java este verificat daca nu cumva incearca sa faca anumite actiuni care ar putea fi daunatoare: violarea restrictiilor de acces, accesul incorect al claselor, depasirea superioara sau inferioara a stivei, folosirea unor parametri incorecti, efectuarea unor conversii ilegale de date, depasirea superioara sau inferioara a indicilor tablourilor etc.
Protectia fisierelor si a retelei locale – clasele nu pot scapa de sub control nici dupa ce au fost incarcate si verificate. In timpul executiei se face o verificare continua pentru a depista cazurile in care clasele incearca sa faca acces la fisiere si la alte resurse din reteaua locala, care nu respecta nivelele de protectie stabilite de utilizatori si de administratori.
Alte caracteristici ce au impus alegerea limbajului Java ca solutie de implementare pentru acest proiect:
Limbaj robust – Java este robust tocmai pentru faptul ca permite scrierea unor programe suple si puternice, care functioneaza corect si nu provoaca daune sistemului. Numai in jurul unui astfel de limbaj se poate construi un mediu robust distribuit pe Internet. Autorii limbajului si-au dat seama ca nu pot crea un astfel de limbaj prin simpla extensie a lui C++. De aceea, au proiectat din temelii un limbaj nou cu aceste calitati. In mediul Java totul se decide in mod dinamic, in timpul executiei, pentru a se evita conflicte cu anumite decizii luate in momentul compilarii. De asemenea, gestiunea memoriei in Java se face automat, nu prin instructiuni scrise in programul sursa ca in C si C++.
Limbaj performant – se stie ca pretul platit pentru a obtine portabilitate, siguranta in functionare si robustete, este pierderea partiala a performantei. Executia interpretativa este mai putin performanta decit executia codului nativ. Totusi, Java este un limbaj performant, aceasta calitate echilibrindu-se cu celelalte enumerate mai sus. Java are incorporate mai multe instrumente prin care cistiga in performanta.
O cale prin care Java ajunge la performante ridicate este facilitatea de multithreading, adica posibilitatea de a incorpora mai multe fire de executie. Este binecunoscut faptul ca, in timpul executiei unui program, timpul in care acest program ocupa unitatea centrala este relativ mic fata de restul timpului in care programul asteapta interventia utilizatorului, sau este ocupat cu accesul la fisiere sau la retea. Din aceasta cauza, in aplicatiile cu un singur fir de executie unitatea centrala ramine neocupata intervale lung de timp. In mediul Java, acest timp in care unitatea centrala este libera este ocupat imediat de alte fire de executie, prin care se realizeaza anumite actiuni de intretinere a sistemului, cum ar fi colectarea reziduurilor. Una din cauzele pentru care aplicatiile se executa uneori cu performante mai scazute in sistemele traditionale, este tocmai lipsa activitatii de intretinere a sistemului. In mediul Java, activitatea de intretinere se face continuu, prin urmare aplicatiile se vor executa intotdeauna cu performante maxime. Mai mult, pentru aceasta activitate de intretinere nu se cheltuieste timp suplimentar, deoarece se realizeaza prin fire de executie care se executa paralel cu aplicatia utilizatorului, ocupind numai timpul in care unitatea centrala ramine nesolicitata.
Java este performant si prin faptul ca programul binar portabil (bytecode) rezultat in urma compilarii este adaptat in timpul executiei la codul nativ al calculatorului real pe care se executa. In anumite contexte, compilarea unor portiuni din programul este aminata pina in momentul executiei. Astfel, in timpul executiei se fac compilari ad-hoc pentru anumite portiuni din program direct in cod nativ, obtinind astfel performante mai bune.
Pentru a cistiga viteza in executie, Java permite si legarea unor rutine C, dar prin folosirea acestui mod de lucru se reduce portabilitatea.
Facilitati de incarcare dinamica – Java este un sistem extensibil in mod dinamic, care poate achizitiona fisiere din orice punct al retelei Internet. Fiind orientat pe obiecte, extensibilitatea dinamica se extinde si asupra obiectelor. In consecinta, un program poate achizitiona in mod dinamic, din orice punct al retelei Internet, clasele de care are nevoie in timpul executiei.
Este cunoscuta problema fragilitatii superclasei in C++. Daca intr-o
superclasa se schimba o variabila sau o metoda, toate subclasele derivate din ea trebuie sa fie recompilate pentru a functiona corect. Acest lucru este necesar deoarece compilatorul C++ face alocarea relativa in cadrul claselor si inlocuieste referintele la variabile si la metode prin adrese relative in cadrul claselor. Daca se schimba o variabila sau o metoda a unei superclase, se schimba imediat alocarea memoriei si adresele relative in cadrul acelei superclase. In aceste conditii, pentru ca subclasele sa aiba referinte corecte la variabile si la metode din superclasa respectiva, trebuie sa fie recompilate. Acesta fiind modul de lucru in C++, o aplicatie care este distribuita ca program executabil trebuie recompilata, in cazul unei modificari in cod, inainte de distributie. Acest lucru compromite ideea de programare orientata pe obiecte intr-un mediu distribuit. Pentru dezvoltarea unei aplicatii in care trebuiesc legate dinamic clase care se pot afla pe orice calculator din Internet, nu exista siguranta ca acele clase vor ramine neschimbate, in timp. In C++, aplicatia nu ar mai functiona si ar trebui sa fie actualizata in urma fiecarei schimbari.
In Java, aceasta problema este rezolvata astfel: compilatorul nu
transforma simbolurile (numele de variabile si metode) in adrese relative. In mediul Java distribuit pe Internet, legarea variabilelor si metodelor se face tirziu, doar in momentul executiei. Compilatorul transmite interpretorului doar referinte simbolice, nu adrese numerice. Interpretorul rezolva referintele simbolice si determina alocarea memoriei claselor in mod dinamic, in timpul executiei. Regasirea variabilelor si a metodelor se face prin nume, nu prin adrese relative. Orice clasa poate fi modificata in orice moment, fara a fi nevoie sa se recompileze toate subclasele sale. De asemenea, dispare necesitatea actualizarii aplicatilor cind apar noi versiuni de biblioteci Java. Pretul platit pentru aceste avantaje este ca interpretorul trebuie sa caute, in timpul executiei, fiecare simbol. Totusi, aceasta cautare se face o singura data pentru fiecare simbol, deoarece interpretorul transforma simbolurile in adrese numerice cind se face alocarea pentru clase, ajungind in acest fel la o viteza de executie apropiata de cea a codului nativ.
In concluzie, avantajul rezolvarii referintelor in momentul executiei
este ca se pot folosi in mod dinamic clase care se pot actualiza tot timpul, fara a exista pericolul ca aceste clase sa afecteze functionarea corecta a aplicatiei. Dupa ce este legata in aplicatie o clasa de pe un alt sistem, proprietarul acelei clase o poate modifica oricind, fara grija ca o aplicatie care foloseste acea clasa va intra in system crash, sau va functiona in mod incorect. Limbajul Java a fost construit in acest spirit pentru a se putea construi in jurul lui un mediu dinamic, distribuit pe Internet.
Mediu distribuit – limbajul Java impune o schimbare in modul de a gindi despre calculul distribuit, fiind o adevarata revolutie pentru Internet. Asa cum calculatoarele personale i-au eliberat pe utilizatori de dependenta fata de un mainframe, limbajul Java elibereaza clientii retelei Internet de anumite servere pe care erau nevoiti sa-si execute aplicatiile zilnice. Cu ajutorul unui browser care permite executia de cod Java se poate realiza transferul de continut executabil, in mod dinamic, de oriunde din Internet pe calculatorul gazda, unde codul este executat. De aceea se poate considera ca functiile de retea sint cele mai importante elemente care trebuie sa fie implementate in mediul Java. La ora actuala exista biblioteci de clase care ofera functii pentru accesul rapid la protocoalele standard Internet. Prin intermediul unei legaturi TCP/IP la reteaua Internet, browserele actuale permit navigarea prin retea si accesul la resursele existente pe diversele masini.
Orientare obiect – Java a fost proiectat din temelii ca limbaj obiectual, ceea ce inseamna ca intr-un program nu exista nimic in afara claselor. Facilitatile obiectuale din Java sint, in esenta, aceleasi din C++, cu unele extensii din Objective C pentru rezolvarea metodelor in mod dinamic. Prin eliminarea pointerilor si a aritmeticii lor precum si a introducerii gestiunii automate a memoriei, se reduce timpul afectat de programator cautarii de erori in programe, in favoarea dezvoltarii de functionalitati pentru aplicatii.
La ora actuala exista implementari ale mediului Java pentru calculatoarele din familia Sun (Sparc/Solaris), pentru cele din familia PC pe care este instalat sistemul de operare Windows (95, 98, NT), Linux sau Solaris, Network Computers, Macintosh..
3.3 Alegerea mediului de dezvoltare
3.3.1 Sockets
Java este un limbaj de programare pentru retele. Acest motiv a facut ca el sa fie prevazut cu un pachet de clase specializate pentru a lucra in retea. S-a pastrat ideea mentinerii unei legaturi intre programele deja dezvoltate si a compatibilitatii cu standardele existente. In cadrul pachetului java.net exista clase care utilizeaza interfata bazata pe sockets. Aceste clase reprezinta o solutie pentru programarea retelelor IP. Este, de asemenea, posibila comunicarea cu aplicatii scrise in alte limbaje de programare, cum ar fi C, care folosesc, la rindul lor, o interfata de tip socket.
Sockets reprezinta o interfata de programare a retelelor de calculatoare, teoretic independenta de protocoalele de comunicatii folosite. In practica, este legata de suita de protocoale TCP/IP. Implementarile acestei interfete se pot gasi in orice sistem de operare care permite conectivitate TCP/IP, cum ar fi Windows si UNIX. Existenta acestei interfete in majoritatea sistemelor de operare a facut posibila portarea usoara a programelor care folosesc retelele IP, conducind la o larga raspindire a acestor programe.
Aplicatiile pot comunica prin protocoale orientate conexiune. O conexiune reprezinta un canal sigur de comunicatie, prin care se transmit mesaje intre doua aplicatii, situate la cele doua extremitati. Protocoalele asigura transmisia corecta si in aceeasi ordine a mesajelor de la emitator la receptor. Odata stabilita conexiunea, aplicatiile pot folosi canalul de comunicatie prin intermediul abstractizarii oferite de fluxurile de date (streams). Astfel, un canal implementeaza doua fluxuri de date unidirectionale, fiecare flux fiind folosit pentru comunicarea intr-un singur sens. Este treaba programelor de sistem care gestioneaza comunicatia in retea sa se ocupe de siguranta in functionare specifica conexiunilor. La fiecare capat al acestui canal exista un socket care permite trimiterea datelor prin canal si receptia informatiilor trimise prin canal catre si de la statia partenera. Orice conexiune este unic determinata de cei doi sockets plasati la capetele conexiunii. Socket-ul este punctul prin care programatorul de aplicatii poate avea acces la conexiune. Pentru a putea transmite date in siguranta, programatorul trebuie doar sa specifice adresa aplicatiei partener, cu care doreste sa comunice, programele de sistem asigurind stabilirea conexiunii si gestiunea acesteia.
O conexiune are trei faze de existenta. Prima este crearea de sockets la capetele conexiunii, raminind in sarcina programeleor de sistem stabilirea conexiunii intre cei doi sockets. In cazul in care operatia de creare de sockets s-a incheiat cu succes, programatorul poate sa obtina referinte la cele doua fluxuri de date (emisie si receptie), asociate capatului sau de conexiune. Cle doua fluxuri vor fi folosite pentru comunicarea cu aplicatia partener. Dupa incheierea comunicatiei prin canal, fiecare partener trebuie sa inchida socket-ul sau, realizindu-se, astfel, inchiderea conexiunii.
In cadrul implementarii standard Java, protocolul folosit pentru implementarea comunicatiei sigure pe baza de conexiuni este TCP (Transmission Control Protocol), unul din protocoalele de baza ale familiei TCP/IP. Acest protocol asigura o comunicare sigura, cu pretul stabilirii de conexiuni inainte de transmisie si al unei rate de transfer mai mici datorata mesajelor de confirmare.
Modul de lucru cu sockets se incadreaza in modelul client-server de scriere de aplicatii. In cadrul acestui model, aplicatiile se impart in doua categorii: servere, adica programe care ofera servicii, si clienti, adica programe care solicita servicii de la servere. Clientii sint cei care initiaza conversatia. Pot exista mai multi clienti care solicita servicii de la un server, deci serverul trebuie sa aiba posibilitatea de a ii diferentia. Se impune ca socket-ul de la capatul serverului sa fie diferit de cel de la capatul clientului. Asimetria provine din cauza modului in care se stabileste legatura intre server si client: clientul creaza un socket caruia ii specifica adresa programului partener. In cadrul initializarii socket-ului, sistemul trimite o cerere de conexiune catre programul cu care clientul vrea sa comunice, adica serverul. Acesta este pregatit sa primeasca cereri de conexiune, si, la acceptarea unei cereri de conexiune, programele sistem creaza socket-ul pereche pe care aplicatia partener il primeste ca rezultat al acceptarii conexiunii. In acelasi timp, sistemul de pe calculatorul server trimite sistemului client un mesaj de confirmare a stabilirii conexiunii, moment in care initializarea socket-ului client se incheie. Deci, in programul client, socket-ul este creat explicit, pe cind, in programul server, socket-ul este creat implicit de catre biblioteca de functii.
Pentru a realiza comunicarea cu un server, clientul are nevoie de doua informatii care compun impreuna adresa serverului: numele gazdei pe care ruleaza serverul si numarul portului pe care serverul asculta cereri de conexiuni.
Comportamentul standard al interfetei sockets permite comunicarea in Internet fara restrictii impuse de ziduri de protectie sau mecanisme criptografice. Orice comportament diferit de cel standard trebuie precizat explicit de programator.
Modul de lucru implicit al interfetei sockets Java este suficient pentru cele mai multe aplicatii. Cu toate acestea, exista aplicatii care nu pot functiona corect pe baza comportamentului implicit al acestei interfete. Este vorba, in principal, de aplicatiile in care este necesara comunicarea peste un zid de protectie (firewall). Pentru aceste aplicatii, interfata sockets trebuie sa aiba un comportament specific, conform cu politica si metodele de asigurare a securitatii impuse in cadrul retelei respective. Schimbarea comportamentului interfetei este posibila prin intermediul anumitor clase din biblioteca java.net.
Inchiderea conexiunii cu aplicatia partener trebuie facuta de catre ultimul utilizator al conexiunii. Java este un mediu in care este posibil ca mai multe fire de executie sa partajeze aceleasi resurse. In cazul in care mai multe fire de executie folosesc o conexiune, faptul ca un anumit fir de executie nu mai foloseste conexiunea nu inseamna automat ca si celelalte fire de executie cu care a cooperat nu mai au nevoie sa comunice. Ramine in sarcina programatorului sa proiecteze aplicatia astfel incit inchiderea legaturii sa se faca doar atunci cind este cazul.
3.3.2 Datagrame
O alta metoda de comunicare in cadrul unei retele o reprezinta datagramele. O datagrama reprezinta o cantitate de informatii trimisa prin intermediul unui pachet independent fata de alte informatii. In cadrul reprezentarii standard Java, datagramele sint transportate prin retea folosind protocolul UDP (User Datagram Protocol), unul din protocoalele de baza din familia de protocoale TCP/IP. Spre deosebire de lucrul cu conexiuni, in care transmisia si receptia sint sigure, datagramele se pot pierde, pot ajunge intr-o ordine diferita fata de cea in care au fost trimise sau pot fi duplicate pe drumul dintre sursa si destinatie. Aplicatiile care folosesc datagrame sint cele pentru care buna functionare nu este influentata de de acest tip de comportament. Avantajele datagramelor sint viteza de transfer mai mare, adaptarea la conditiile de moment ale retelei (congestii, legaturi temporar indisponibile). Datagramele pot fi trimise cu destinatii multiple, ceea ce inseamna ca acelasi pachet poate fi receptionat de mai multe statii simultan.
Spre deosebire de interfata sockets de tip conexiune, in cazul datagramelor se remarca lipsa fazelor de stabilire a conexiunilor si de obtinere a fluxurilor de intrare si iesire, trimiterea si receptia individuala a pachetelor si inexistenta unui socket de tip special pentru ascultarea cererilor de stabilire de comunicatie.
Desi tipul de comunicatie este diferit, modul de identificare aprogramelor de pe o gazda este similar cu cel folosit pentru conexiuni. Astfel, o aplicatie care receptioneaza datagrame este identificata prin adresa IP a gazdei pe care ruleaza, la care se adauga numarul portului pe care aplicatia asculta datagrame. Socket-ul care este folosit pentru receptia sau transmisia datagramelor este de acelasi tip atit pentru client, cit si pentru server.
Datagramele sint folosite, de obicei, pentru implementarea unei comunicatii de tip cerere raspuns. Cererea trimisa de client va fi urmata de un raspuns al serverului, raspuns din care se va putea deduce cererea la care s-a dat raspuns. Raspunsul poate fi privit ca o confirmare a receptionarii cererii, fapt care elimina necesitatea confirmarilor implicite. In cazul in care un pachet se pierde, clientul va retrimite cererea dupa trecerea unui interval de timp.
Printre noutatile introduse de jdk1.1.x se numara si un nou tip de socket de tip datagrama, si anume cel pentru trimiterea multipla (multicast).
Tinind seama de faptul ca interfata sockets de tip conexiune garanteaza transmisia in ordine si corecta a pachtelor de date, in timp ce datagramele nu garanteaza acest lucru, rezulta ca fiind mai potrivita prima varianta de implementare. Mentinerea conexiunii intre client si server este mai importanta decit o crestere de viteza a transmisiei in detrimentul sigurantei.
3.3.3 RMI
In ceea ce priveste sistemele distribuite, este necesar ca procesele de calcul ce se afla in desfasurare la adrese diferite, posibil pe masini diferite, sa fie capabile sa comunice intre ele. Mecanismul sockets, existent in Java, reprezinta o metoda de baza utilizata pentru comunicare. Interfata sockets este suficient de flexibila si suficienta pentru comunicare, la modul general. Totusi, acest mecanism reclama angajarea clientului si a serverului in protocoale la nivel de aplicatie pentru a putea codifica si decodifica mesaje, iar proiectarea si constructia acestor protocoale este dificila. Si poate fi generatoare de erori.
O alternativa la sockets este RPC (Remote Procedure Call), care abstractizeaza interfata de comunicare la nivelul apelului de procedura. In loc de a lucra direct cu sockets, programatorul are impresia ca realizeaza un apel local de procedura, cind, de fapt, argumentele apelului sint impachetate si expediate catre tinta la distanta a apelului. Sistemele RPC codifica argumentele si intorc valori utilizind un mod de reprezentare externa a datelor, cum ar fi XDR.
Totusi, RPC nu este solutia ideala in cazul sistemelor distribuite de obiecte, unde se doreste comunicatia, la nivelul programului, intre obiecte aflate in diferite spatii de adrese. In cazul acestor sisteme se foloseste mecanismul RMI (Remote Method Invocation). Acest mecanism implica existenta unui surogat local (stub) care se ocupa de apelul unui obiect la distanta.
La aparitia sa, despre limbajul Java s-a spus ca poate fi utilizat si in programarea distribuita. Nu existau, insa, mecanismele explicite pentru acest tip de calcul. Introducerea mecanismului RMI a permis utilizarea limbajului si pentru acest tip de programare.
Se considera ca mecanismul RMI se bazeaza pe relatia client-server. El a fost special destinat pentru a fi utilizat in mediul Java. RMI pleaca de la presupunerea ca mediul JVM este omogen, putind, astfel, sa beneficieze de avantajele modelului de obiect Java.
Citeva din dezideratele sistemului de obiecte distribuite, in Java, ar fi:
Suport pentru apel la distanta unitar asupra obiectelor aflate in diferite JVM;
Mecanismul callback in relatia server – applet;
Integrarea modelului obiectelor distribuite in limbajul Java are loc intr-un mod natural, pastrindu-se, in mare masura, semantica limbajului;
Diferentele dintre modelul de obiecte distribuite si obiectele locale sint transparente pentru utilizator;
Scrierea aplicatiilor distribuite este simpla si sigura;
Pastrarea consistentei tipurilor furnizata de mediul de executie Java;
Permite diferite semantici de referinte pentru obiectele la distanta; de exemplu live (nonpersistent), persistent, lazy activation;
Securitatea mediului Java este asigurata de catre managerii de securitate si de catre incarcatorii de clase.
Indeplinirea acestor deziderate face din RMI un model simplu de utilizat si
natural, in sensul ca se integreaza foarte bine in limbaj.
In cadrul modelului de obiecte distribuite Java, un obiect la distanta (remote object) este acela ale carui metode pot fi apelate din cadrul altei JVM, posibil de pe alta masina. Un astfel de obiect este descris de una sau mai multe interfete la distanta (remote interfaces), care sint interfete Java ce declara metode ale obiectului la distanta.
RMI (Remote Method Invocation) reprezinta apelul unei metode a unei interfete la distanta a unui obiect la distanta. Un apel de metoda a unui obiect la distanta are aceasi sintaxa cu apelul unei metode dintr-un obiect local.
Aplicatiile RMI sint vazute ca doua programe distincte: un server si un client. Un caz tipic de aplicatie server creaza un numar de obiecte la distanta, face accesibile referintele lor si asteapta apelul metodelor acestor obiecte, din partea clientilor. O aplicatie client tipica obtine o referinta a unui obiect la distanta si apoi invoca metodele acestora. RMI furnizeaza mecanismul prin care clientul si serverul comunica si transfera informatie. Acesta este un caz de aplicatie cu obiecte distribuite.
Pentru ca o aplicatie sa poata fi considerata distribuita, ea trebuie sa parcurga anumite etape:
Localizarea obiectelor la distanta – Pentru aceasta, aplicatiile pot utiliza unul din cele doua moduri de obtinere a referintelor obiectelor la distanta: inregistrarea obiectelor aplicatiei prin intermediul serviciului de nume al RMI, rmiregistry, sau transmiterea ca parametru sau ca rezultat al apelului unei metode, a referintei unui obiect la distanta;
Comunicarea cu celelalte obiecte la distanta – Detaliile comunicarii intre obiectele la distanta cad in sarcina RMI; pentru programator, acest tip de comunicare arata ca si apelul standard al unei metode Java;
Incarcarea codului pentru obiectele care sint trimise ca parametri sau intoarse ca rezultat al apelului de metoda – RMI permite apelantului sa transmita obiecte Java pure catre obiectul la distanta, furnizind mecanismul necesar pentru incarcarea codului obiectului si pentru transmiterea sa.
3.3.3.1 Asemanari intre modelul obiectelor distribuite si cel al obiectelor locale
O referinta catre un obiect la distanta poate fi transmisa ca argument sau intoarsa ca rezultat in orice apel de metoda (local sau la distanta);
Un obiect la distanta poate suporta cast catre orice interfata la distanta suportata de catre implementare, folosind sintaxa specifica Java;
Operatorul instanceof poate fi folosit pentru a testa interfata la distanta implementata de catre obiectul la distanta.
3.3.3.2 Deosebiri intre modelul obiectelor distribuite si cel al obiectelor locale:
Clientii unui obiect la distanta interactioneaza cu interfetele la distanta, nu cu implementarile acestor interfete;
Argumentele si rezultatele apelurilor, care nu sint obiecte la distanta, sint transmise prin copiere, nu prin referinta. Aceasta deoarece referintele catre obiecte au semnificatie doar in cadrul aceleasi JVM;
Un obiect la distanta este trimis prin referinta, nu prin copierea implementarii sale;
Semantica anumitor metode ale clasei java.lang.Object este specializata pentru obiecte la distanta;
Datorita complexitatii crescute a mecanismului RMI, in cazul esecului unui apel la distanta vor aparea exceptii corespunzatoare, de care clientul trebuie sa tina seama.
Un obiect local trimis ca parametru sau intors ca rezultat al apelului unei
metode este transmis prin copiere. Acest lucru inseamna ca obiectul este serializat utilizand mecanismul Java Object Serialization.
Cind un obiect la distanta este transmis ca parametru sau intors ca rezultat al
unui apel, se transmite, de fapt un stub. Un obiect la distanta transmis ca parametru poate sa implementeze doar interfete la distanta.
In Java se utilizeaza conceptul de Integritate Referentiala (Referential
Integrity). Acest lucru se refera la faptul ca transmiterea a doua referinte (ca parametri sau valoare intoarsa) catre acelasi obiect de la o VM la alta VM in cadrul aceluias apel de metoda, si acele referinte refera acelasi obiect pe masina sursa, ele vor referi o singura copie a obiectului pe masina destinatie. Acest lucru se traduce prin aceea ca, in cadrul unui apel de metoda, sistemul RMI mentine integritatea referentiala pentru obiectele transmise ca parametri sau intoarse ca valori ale apelului.
Atunci cind un obiect este trimis de la o VM la alta intr-un apel de metoda la distanta, sistemul RMI adauga la descriptorul clasei cu adresa URL a clasei astfel incit aceasta sa poata fi incarcata de catre receptor. Clasele trebuie sa fie aduse pe masina destinatie la cerere, in timpul apelului.
Pentru a localiza obiectele la distanta este furnizat un serviciu de nume. In obiectul ce implementeaza acest serviciu este pastrata o tabele cu referinte catre obiecte la distanta. Pentru a fi inregistrat, se utilizeaza metode, bazate pe URL, ale unei clase specializate.
Pentru ca un client sa poata apela metode ale unui obiect la distanta, el trebuie, mai intii, sa obtina o referinta catre acel obiect. O astfel de referinta poate fi obtinuta ca parametru sau valoare intoarsa intr-un apel de procedura la distanta.
RMI utilizeaza un mecanism standard (utilizat in sistemele RPC) pentru a comunica cu alte obiecte la distanta: stub si skeleton. Un stub pentru un obiect la distanta se comporta ca un reprezentant sau proxy al acelui obiect, din punctul de vedere al clientului. Apelantul invoca o metoda a stub-ului local care este responsabil pentru ducerea la indeplinire a acelui apel. In RMI, un stub al unui obiect la distanta implementeaza acelasi set de interfete la distanta pe care le implementeaza obiectul insusi.
In momentul cind este apelata o metoda a unui stub, se intimpla urmatoarele lucruri:
Se initializeaza conexiunea cu VM ce contine obiectul la distanta;
Are loc procesul marshalling – scriere si transmitere a parametrilor catre VM de la distanta;
Se asteapta rezultatul apelului metodei invocate;
Are loc procesul unmarshalling – citire a rezultatului sau exceptiei returnate.
Este intoarsa valoarea catre apelant.
Stub-ul ascunde programatorului serializarea parametrilor si comunicatia la nivel de retea pentru a putea prezenta un mecanism de apel mai simplu.
In cadrul VM la distanta, fiecare obiect la distanta poate avea un skeleton corespunzator. Acesta este raspunzator pentru dispecerizarea apelului catre actuala implementare a obiectului la distanta.
Cind un skeleton primeste un apel de metoda, au loc urmatoarele actiuni:
Are loc procesul unmarshalling – citire a parametrilor pentru metoda la distanta;
Este invocata metoda actualei implementari a obiectului la distanta;
Are loc procesul marshalling – scriere si transmitere a rezultatului (valoare intoarsa sau exceptie) catre apelant.
In sistemele distribuite, ca si in cele locale, este de dorit sa fie eliminate acele obiecte la distanta care nu mai sint referite de catre nici un client. RMI foloseste un algoritm de garbage collection de tip reference-counting, mostenit din Modula-3. Mediul RMI detine informatii asupra tuturor referintelor active din sistem. Cind un obiect la distanta nu mai este referit de catre nici un client, mediul RMI il marcheaza. Acest lucru permite colectorului local sa elimine obiectele astfel marcate. Algoritmul de garbage collection local interactioneaza cu cel distribuit pentru a pastra consistenta informatiilor si actiunilor. Obiectele la distanta sint eliminate doar atunci cind nu mai exista nici referinte locale si nici la distanta, catre ele.
Este de remarcat faptul ca, in cazul in care exista o partitie de retea intre client si un server de obiecte la distanta, este posibil sa apara o eliminare prematura a obiectului. Din aceasta cauza, referintele la distanta nu pot garanta integritatea referentiala; poate exista, astfel, o referinta la distanta ce refera un obiect inexistent. La o incercare de utilizare a lui se va genera o exceptie.
RMI furnizeaza un mecanism pentru incarcarea dinamica a definitiilor de clase in cazul apelurilor de metode la distanta ce se fac din retea. Acesta include descarcarea dinamica a claselor stub ce corespund unor anumite implementari ale obiectelor la distanta.
Nivelul transport al RMI incearca sa deschida sockets catre masinile din Internet. Multe retele Intranet au firewalls care nu permit, totusi, acest lucru. Nivelul transport implicit furnizeaza doua alternative bazate pe protocolul HTTP care permit unui client din spatele unui firewall sa apeleze o metoda a unui obiect la distanta, ce se afla de cealalta parte a firewall-ului.
Pentru a trece de firewall, nivelul transport inglobeaza apelul RMI in protocolul HTTP, ce poate patrunde dincolo de firewall. Apelul este, de fapt, incorporat intr-o cerere HTTP POST, iar informatia returnata se afla in corpul raspunsului HTTP.
3.3.3.3 Activarea obiectelor la distanta
Sistemele de obecte distribuite sint construite sa suporte obiecte persistente. Tinind seama de faptu ca intr-un astfel de sistem se pot acumula milioane de astfel de obiecte, se pune problema ca obiectele sa nu devina active si sa ramina in aceasta stare pentru o perioada indefinita de timp. Pe de alta parte, clientii sint interesati sa pastreze referintele catre obiecte persistente, astfel incit sa poata relua comunicarea chiar si dupa o cadere a sistemului. Totusi, referintele obiectelor sint viabile atita timp cit obiectul distribuit respectiv exista. Activarea obiectelor reprezinta un mecanism prin care se furnizeaza referinte persistente catre obiecte si se administreaza executia implementarilor acestor obiecte. In RMI, acest mecanism permite obiectelor sa devina active atunci cind sint apelate pentru prima data metode ale lor.
Un obiect activ reprezinta un obiect la distanta care este instantiat si exportat intr-o JVM a unui sistem. Un obiect pasiv reprezinta un obiect care nu este inca instantiat (sau exportat) intr-o VM, dar care poate fi adus intr-o stare activa. Transformarea unui obiect pasiv intr-unul activ se numeste activare.
Protocoalele folosite de RMI sint Java Object Serialization si HTTP. Primul este utilizat pentru procesele marshalling si unmarshalling. Cel de-al doilea este utilizat la apelurile de metode si obtinerea rezultatului in corpul unei cereri POST urmate de raspuns.
3.3.4 CORBA
3.3.4.1 Prezentare generala
Un sistem de obiecte este o colectie de obiecte care ii izoleaza pe solicitantii de servicii (clienti) de furnizorii de servicii printr-o interfata. Clientii sint izolati de implementarile serviciilor in ceea ce priveste reprezentarea datelor si codul executabil.
Modelul obiectual descrie, mai intii, concepte ce au sens pentru client, incluzind crearea si identitatea, cererile si operatiile, tipurile si signaturile. Mai apoi, descrie conceptele legate de implementarile obiectelor, incluzind notiuni ca metode, motoare de executie si activare.
Exista si alte caracteristici ale sistemelor de obiecte care se regasesc in afara domeniului modelului obiectual. Se pot enumera: arhitectura aplicatiei, obiectele compuse, legaturi, copierea obiectelor, tranzactiile, detaliile de control ale structurii.
Modelul obiectual CORBA este clasic: clientul trimite mesaje catre un obiect. Conceptual, obiectul interpreteaza mesajul pentru a decide ce actiune sa intreprinda. In modelul obiectual clasic, un mesaj identifica un obiect si zero sau mai multi parametri actuali. La fel ca in cele mai multe modele clasice de obiecte, un prim si distinct parametru este necesar, pentru a identifica operatia care trebuie sa se execute. Interpretarea mesajului de catre obiect implica selectia unei metode, bazate pe operatia specificata.
Din punct de vedere semantic, un sistem de obiecte furnizeaza servicii catre clienti. Un client al unui serviciu este o entitate capabila sa solicite acel serviciu.
Un sistem de obiecte include entitati cunoscute ca fiind obiecte. Un obiect este o entitate incapsulata si identificabila, care furnizeaza unul sau mai multe servicii ce pot fi solicitate de catre client.
Clientii solicita servicii prin emiterea de cereri. O cerere este un eveniment (ceva care survine la un moment dat). Informatia asociata cererii consista in: o operatie, un obiect tinta, zero sau mai multi parametri (actuali) si, optional, un context asociat cererii.
O valuare reprezinta orice ar putea fi considerat ca parametru intr-o cerere.
O referinta de obiect este o valuare care denota un anume obiect.
O cerere implica efectuarea unui serviciu pentru client. Un posibil rezultat al acestei actiuni este returnarea unui rezultat sau a unei exceptii.
Obiectele pot fi create si distruse. Din punctul de vedere al clientului, nu exista un mecanism special pentru crearea sau distrugerea obiectelor. Acestea sint create si distruse ca rezultat al emiterii unor solicitari. Rezultatul crearii unui obiect este remis clientului sub forma unei referinte care indica noul obiect.
Un tip este o entitate identificabila cu un predicat asociat, definit pe o multime de valori. O valuare satisface un tip daca predicatul este adevarat pentru acea valoare. O valoare care satisface un tip este numita membra a tipului.
O operatie este o entitate identificabila care indica un serviciu ce poate fi solicitat si poate fi identificata printr-un identificator al operatiei. O operatie nu este o valoare.
Implementarea unui sistem de obiecte duce la indeplinire activitatile de calcul necesare pentru a raspunde serviciilor solicitate. Aceste activitati pot insemna calculul rezultatului cererii si actualizarea starii sistemului. Modelul de implementare este alcatuit din doua parti: modelul de executie si modelul de constructie. Primul descrie cum sint realizate serviciile iar al doilea, cum sint definite serviciile.
3.3.4.2 Arhitectura CORBA
Arhitectura CORBA (Common Object Request Broker Architecture) este structurata astfel incit sa permita integrarea unei largi varietati de sisteme de obiecte. ORB (Object Request Broker) este responsabil pentru toate mecanismele necesare localizarii implementarii obiectului solicitat in cerere, sa pregateasca implementarea pentru a putea primi cererea si sa comunice datele, pentru a rezolva cererea. Interfata pe care o vede clientul este complet independenta de locul in care se afla implementarea obiectului, in ce limbaj de programare este implementat sau orice alt aspect care nu se reflecta in interfata obiectului.
Iata cum se prezinta scheme unei cereri adresate de catre client, serverului:
Fig.2 – Cerere adresata de catre client serverului, prin intermediul ORB
Pentru a face o cerere, clientul poate utiliza DII (Dynamic Invocation Interface) sau un stub OMG IDL. Clientul poate interactiona direct cu ORB pentru anumite functii.
Serviciul OI (Object Implementation) primeste o cerere fie prin skeleton-ul OMG IDL, fie printr-un skeleton dinamic. OI poate apela OA (Object Adapter) si ORB in timp ce proceseaza o cerere sau la alt moment de timp.
Iata cum se prezinta structura unei astfel de cereri:
Fig. 3 – Structura interfetei unei cereri catre ORB
Definitia unei interfete de obiect poate fi facuta in doua feluri. Interfetele pot fi definite static, in OMG IDL (OMG Interface Definition Language). Acest limbaj defineste tipurile de obiecte in concordanta cu operatiile ce pot fi aplicate asupra lor si cu parametrii acelor operatii. Alternativ, sau in completare, interfetele pot fi adaugate la serviciul IR (Interface Repository) . Acest serviciu reprezinta componentele unei interfete ca obiecte, permitind accesul la aceste componente la executie. In orice implementare ORB, IDL si IR au o putere de expresie echivalenta..
Clientul emite o cerere avind acces la o referinta a unui obiect si stiind tipul obiectului si operatiile pe care vrea sa le execute. Clientul initiaza cererea prin apelul unei rutine stub care este specifica obiectului sau construind acest apel in mod dinamic. DII si interfata stub satisfac aceeasi semantica pentru cerere, iar destinatarul mesajului nu poate spune cum a fost invocata cererea.
ORB localizeaza codul implementarii corespunzatoare, transmite parametrii si transfera controlul catre OI printr-un skeleton IDL sau un skeleton dinamic. Skeleton-ul este specific interfetei si OA. In timpul executarii cererii, implementarea obiectului poate obtine diverse servicii de la ORB prin intermediul OA. Cind cererea este satisfacuta, controlul si eventualele rezultate sint transmise clientului.
OI poate decide ce ORB sa foloseasca. Aceasta decizie se bazeaza pe tipul serviciilor pe care le solicita OI.
3.3.4.3 ORB
In cadrul arhitecturii, ORB nu trebuie neaparat sa fie implementat ca o singura componenta; este, mai degraba, definit prin interfetele sale. Orice implementare ORB care furnizeaza interfata potrivita, este acceptabila. Interfata este organizata in trei categorii:
Operatii care sint aceleasi pentru toate implementarile ORB;
Operatii specifice unor tipuri particulare de obiecte;
Operatii specifice unor anumite stiluri de implementari de obiecte.
Diversele implementari ORB formeaza, laolalta cu compilatoarele IDL, depozite (repositories) si diferite OA, furnizeaza un set de servicii clientilor si implementarilor obiectelor care au diverse proprietati si calitati.
Pot exista multiple implementari ORB care au diverse reprezentari pentru referinte de obiecte si diferite moduri de a rezolva apelurile. Un client poate avea acces, simultan, la doua referinte de obiecte ce se afla in administrarea a doua implementari ORB distincte; acele ORB trebuie sa fie capabile sa distinga intre cele doua referinte. Acest lucru nu cade in sarcina clientului.
3.3.4.4 Clienti
Un client al unui obiect are acces la referinta obiectului si invoca operatii asupra obiectului. Clientul cunoaste doar structura logica a obiectului, in concordanta cu interfata acestuia si afla comportamentul implementarii in urma invocarii metodelor. Clientii vad obiectele si interfetele ORB prin intermediul limbajelor de programare mapate pe IDL. Clientii sint intru totul portabili si trebuie sa fie capabili sa lucreze cu orice ORB care suporta o mapare pe limbajul respectiv si orice obiect care implementeaza interfata cunoscuta clientului. Clientii nu cunosc implementarea obiectului, care OA este utilizat de implementare, sau care ORB este folosit pentru accesul la aceasta.
3.3.4.5 Implemetarile obiectelor
Implementarea unui obiect furnizeaza semantica obiectului, de obicei prin definirea datelor pentru instantele obiectului si a codului pentru metodele obiectului.
Poate exista o varietate de implementari ale obiectelor, incluzind servere separate, biblioteci, baze de date etc.
In general, implementarile obiectelor nu depind de ORB sau de modul in care clientul invoca obiectul. Implementarile obiectelor pot selecta interfete catre serviciile dependente de ORB prin intermediul OA.
3.3.4.6 Referinte de obiecte
O referinta de obiect reprezinta informatia necesara pentru a specifica obiectul in cadrul unui ORB. Si clientul, si implementarea obiectului sint izolati de reprezentarile referintelor in limbajul respectiv.
Reprezentarea unei referinte de obiect transmisa unui client este valida doar pe durata existentei clientului.
Toate implementarile ORB trebuie sa ofere aceeasi mapare dpdv al limbajului pentru o referinta de obiect, pentru un anume limbaj de programare. Acest lucru permite unui program scris intr-un anume limbaj sa acceseze referinte de obiecte independent de ORB.
Exista o referinta distincta, diferita de toate celelalte referinte, care indica un obiect virtual (inexistent).
3.3.4.7 OMG IDL
OMG IDL (OMG Interface Definition Langage) defineste tipurile de obiecte prin specificarea interfetelor lor. O interfata consta intr-un set de operatii si parametrii acelor operatii. Desi obiectele cu care lucreaza ORB sint descrise de IDL, codul IDL nu este necesar pentru ca obiectele sa functioneze. Este necesara doar informatia sub forma de rutine stub sau DII.
IDL este mijlocul prin care o anumita implementare de obiect specifica clientilor potentiali ce operatii sint disponibile si cum trebuie apelate. Pornind de la definitiile IDL, este posibil sa se mapeze obiecte CORBA pe un anumit limbaj de programare sau sistem de obiecte.
O mapare particulara a OMG IDL pe un limbaj de programare trebuie sa fie aceeasi pentru toate implemetarile ORB. Maparea pe un anumit limbaj include structura interfetei stub a clientului, DII, implementarea skeleton-ului, OA si interfata directa ORB.
3.3.4.8 DII
DII (Dynamic Invocation Interface) permite constructia dinamica a invocarii asupra unui obiect. Clientul specifica obiectul ce trebuie invocat, operatia ce trebuie efectuata si setul de parametri necesari apelului. Acest lucru se realizeaza prin unul sau mai multe apeluri. Informatiile despre operatia de efectuat si despre tipurile parametrilor se pot obtine la momentul executiei din IR sau alta sursa.
3.3.4.9 Skeleton
Pentru o mapare a IDL pe un anumit limbaj va exista o interfata catre metodele ce implementeaza fiecare tip de obiect.. ORB utilizeaza aceasta interfata pentru a apela metodele din implementarea obiectului. Existenta unui skeleton nu implica si existenta unui stub client.
Se poate scrie un OA care sa nu foloseasca skeleton-ul pentru a apela metodele din implementarea obiectului.
3.3.4.10 DSI
DSI (Dynamic Skeleton Interface) este o interfata ce permite abordarea dinamica a invocarilor de obiecte. In loc sa se utilizeze un skeleton care este specific unei anumite operatii, implementarea obiectului este accesata prin intermediul unei interfete care permite apelul metodei intr-o maniera asemanatoare cu cea a clientului ce utilizeaza DII. Informatiile despre parametri se pot obtine in mod static sau dinamic (prin IR). Codul implementarii trebuie sa furnizeze descrierei ale tuturor parametrilor catre ORB, iar ORB furnizeaza valorile oricaror parametri de intrare utilizati de operatie. Implementarea furnizeaza parametrii de iesire sau o exceptie, catre ORB, dupa efectuarea operatiei.
Dynamic Skeleton poate fi invocat atit prin stub-ul client, cit si prin DII; oricare varianta produce acelasi rezultat.
3.3.4.11 OA
OA (Object Adapter) reprezinta un prim mod prin care o implementare de obiect acceseaza serviciile oferite de ORB. Serviciile oferite de catre ORB prin intermediul OA pot include: generarea si interpretarea referintelor de obiecte, apeluri de metode, securitatea interactiunilor, activarea si dezactivarea obiectelor si implementarilor, maparea referintelor de obiecte pe implementari si inregistrarea implementarilor.
OA se mai ocupa si de probleme ca: durata de viata a obiectelor, politici, stiluri de implementare sa.
3.3.4.12 IR
IR (Interface Repository) este un serviciu care furnizeaza obiecte persistente ce reprezinta informatie IDL intr-o forma disponibila la executie. Informatiile din IR pot fi utilizate de catre ORB pentru a rezolva cereri. Mai mult, utilizind informatiile din IR, este posibil pentru un program sa lucreze cu un obiect a carui interfata nu era cunoscuta atunci cind programul a fost compilat; programul va putea detrmina, totusi, ce operatii asupra obiectului sint valide si sa le invoce.
In IR se pot stoca informatii pentru depanare, biblioteci, stubs si skeletons, rutine diverse.
3.3.4.13 Implementation Repository
Implementation Repository contine informatii care permit ORB sa localizeze si sa activeze implementarile obiectelor. In mod obisnuit, instalarea implementarilor obiectelor si controlul politicilor de activare si executie a acestor implementari se face prin operatii asupra acestui sistem.
In Implementation Repository se stocheaza si informatii asociate implementarilor obiectelor ORB, cum ar fi: informatii pentru depanare, control administrativ, alocarea resurselor, securitate etc.
3.3.4.14 Tipuri de ORB
Client- and Implementation-resident ORB;
Server-based ORB;
System-based ORB;
Library-based ORB.
3.3.4.15 Integrarea altor sisteme de obiecte
Arhitectura CORBA este proiectata sa permita interoperabilitatea in cadrul unei multimi eterogene de sisteme de obiecte. Se doreste ca obiectele ce compun acele sisteme sa poata fi accesate prin ORB.
3.3.5 DCOM
DCOM, adesea denumit si ‘COM on the wire’, suporta obiecte la distanta care comunica prin intermediul unui protocol numit Object Remote Procedure Call (ORPC). Nivelul ORPC este construit la suprafata nivelului RPC al DCE si interactioneaza cu serviciile COM. Un server DCOM este o sectiune de cod capabila sa ofere servicii prin intermediul unor obiecte de un tip particular, in momentul executiei. Fiecare server DCOM poate suporta multiple interfete, fiecare reprezentind un comportament diferit al obiectului. Un client DCOM apeleaza metodele oferite de serverul DCOM prin obtinerea unui pointer catre una din interfetele obiectului pus la dispozitie de server. Dupa ce obtine acest pointer, clientul poate apela metodele ca si cum obiectul s-ar afla in spatiul local de adresare. Datorita faptului ca specificatia COM este realizata la nivel binar, li se permite componentelor oferite de serverul DCOM sa fie scrise in diverse limbaje, ca C++, Java, Delphi, Visual Basic sau COBOL. Daca o platforma suporta serviciile COM, atunci DCOM poate fi utilizat pe acea platforma. La momentul actual, DCOM este foarte utilizat pe platformele Windows. Ca producatori importanti se pot enumera:
Software AG, cu produsul EntireX pentru UNIX, Linux si platforme mainframe;
Digital, pentru platforma Open VMS;
Microsoft, pentru platformele Windows si Solaris.
3.3.6 Citeva diferente intre cele trei implementari
Atit CORBA, cit si Java/RMI suporta mostenirea multipla la nivelul IDL sau al interfetei (RMI). In ceea ce priveste DCOM, in locul suportului pentru mostenire multipla, COM utilizeaza notiunea de obiect ce are mai multe interfete pentru a realiza aceleasi deziderate.
O diferenta intre CORBA (si Java/RMI) IDL, pe de-o parte, si COM IDL, pe de cealalta parte, este ca cele dintii pot specifica exceptii in IDL, pe cind DCOM nu. In CORBA, compilatorul IDL genereaza informatii de tip pentru fiecare metoda a unei interfete si le stocheaza in Interface Repository (IR). Un client poate, astfel, sa interogheze IR, la executie, pentru a obtine informatii despre o anumita interfata si sa foloseasca acele informatii pentru a construi in mod dinamic un apel de metoda a unui obiect CORBA. Apelul se realizeaza prin intermediul interfetei dinamice, DII. In mod similar, de partea serverului, Dynamic Skeleton Interface(DSI) permite clientului sa invoce o operatie asupra unui obiect CORBA la distanta, obiect ce nu are, la compilare, informatii asupra tipului obiectului pe care il implementeaza.
Spre deosebire de celelalte doua, Java/RMI utilizeaza un fisier .java pentru a-si defini interfata la distanta. Aceasta interfata va asigura consistenta tipurilor intre clientul Java/RMI si obiectul server Java/RMI.
3.3.7 Implementari
Trei dintre cele mai populare implementari ale modelului obiectelor distribuite sint:
Produsul Microsoft Distributed Component Object Model (DCOM);
Produsul OMG Common Object Request Broker Architecture (CORBA);
Produsul Javasoft Java / Remote Method Invocation (Java/RMI).
3.3.8 Decizia
Pentru implementare s-a ales CORBA, care ofera citeva facilitati in plus fata de celelalte doua solutii:
Posibilitate de interoperare cu alte tipuri de sisteme de obiecte (DCOM, DCE);
Arhitectura middleware pentru sisteme distribuite eterogene;
Specificare standardizata;
Posibilitatea scrierii aplicatiilor in diferite limbaje de programare;
Mediu foarte potrivit pentru aplicatii client – server scalabile;
O mare varietate de implementari, disponibile, gratis, prin Internet.
4. Solutia de implementare
Solutia initial aleasa pentru implementarea CORBA a fost ORBacus.
Produsul asigura o implementare completa a serviciilor specificate de standardul OMG: serviciu de nume, de evenimente, DII si DSI, trading etc. Acest produs poate fi obtinut in doua variante, pentru Windows si pentru Linux, sub forma de surse Java. Neajunsurile ce au decis abandonarea acestei implementari au fost:
– varianta pentru Windows poate fi compilata doar sub Windows NT, folosind compilatorul din Visual C++; aceasta, deoarece command.com din Windows 95 sau 98 nu are toate functionalitatile interpretorului din NT; mai este necesar si utilitarul nmake; am considerat ca aceste cerinte ar restrictiona numarul potentialilor utilizatori, si prin prisma faptului ca sint necesare si alte resurse decit implementarea in sine. Un alt argument care a dus la abandonarea acestei solutii a fost lipsa mediului integrat care ar permite realizarea facila a unei interfete vizuale de calitate;
– varianta pentru Linux, ce ar fi reprezentat o a doua solutie, a fost abandonata datorita imposibilitatii utilizarii compilatorului idltojava, ca si datorita aceleiasi lipse a mediului integrat.
Alta posibilitate de implementare o reprezinta implementarea ORB a firmei Sun, existenta in pachetul jdk1.2; in pachetul furnizat pentru CORBA se afla un serviciu de nume; nu este oferita implementarea DII si DSI, desi acest lucru nu reprezinta un inconvenient major; tinind seama de faptul ca versiunea jdk1.2 nu este bine pusa la punct in toate amanuntele (probleme in legatura cu thread-urile, in versiunea pentru Linux), s-a impus folosirea claselor din jdk1.1.x.
Solutia adoptata, in final, a fost implementarea ORB a firmei Visigenic, incorporata in mediul de dezvoltare JBuilder 2.0; acest pachet de programe ofera o solutie completa de implementare a specificatiei OMG; in plus, este oferit un mediu de dezvoltare foarte puternic pentru realizarea aplicatiilor client-server, existind facilitati de depanare, rulare pas cu pas, help on-line, navigare facila intre diversele structuri; un lucru foarte important este posibilitatea de a realiza, relativ simplu, o interfata grafica evoluata, beneficiind de biblioteci variate (jbcl, swing, awt etc). Versiunea folosita este jdk1.1.6. Proiectele realizate pot fi rulate, in principiu, pe orice platforma care are o implementare de JVM, acest lucru fiind asigurat de serviciul deployment care asigura constructia ierarhiei ce contine toate clasele (diferite de cele native jdk) utilizate in aplicatie. Conceptul de portabilitate este, astfel, satisfacut. Este de remarcat si partea de documentatie, variata si bogata in exemple executabile. Sistemul de navigare intre diferitele sectiuni si in cadrul ierarhiei de clase asigura o regasire rapida a informatiilor necesare.
Pentru realizarea interfetei grafice au fost preferate clasele oferite de biblioteca JBCL. In multe cazuri, componentele JBCL sint similare celor AWT si sint construite pe arhitectura Swing. In plus, acestea au avantaje fata de cele din urma, in ceea ce priveste diversitatea si functionalitatile oferite. De exemplu, un buton JBCL permite afisare de imagine si text, pe cind butoanele AWT sau Swing permit doar text; un alt avantaj il reprezinta diversitatea de containere, mai mare la JBCL, care permit o mai buna organizare a spatiului vizual ce trebuie gestionat.
4.1 Consideratii asupra implementarii
Programul este vazut ca un sistem alcatuit din mai multe module. Exista un modul central, numit manager, cu rol de coordonare si decizie in cadrul sistemului. El este impartit, in mod formal, in doua sectiuni, fiecare tratind un alt aspect al comunicatiei. Cele doua sectiuni se ocupa, respectiv, de chat (discutii sub forma de text) si de transfer de informatie in format grafic. Managerul nu controleaza transferul de informatie sub forma de continut executabil, acesta fiind lasat la latitudinea clientilor. Iata cum se prezinta, schematic, arhitectura aplicatiei:
Fig. 4 – Arhitectura aplicatiei
Arhitectura poate fi vazuta ca fiind de tipul client-server. Programele client ruleaza pe masinile locale si solicita servicii, prin intermediul retelei Internet, server-ului. Fiecare modul, dintre cele doua prezentate mai sus, are un corespondent (formal) pe masina client, cu care dialogheaza in vederea realizarii functionalitatii specifice. La acestea doua se adauga si un modul ce se ocupa de sectiunea demo. Pe masina server, programele ruleaza algoritmi ce au rolul de a implementa politici de alocare a drepturilor catre clienti. Clientii interactioneaza cu modulele locale prin intermediul unei interfete grafice, solicitind servicii catre server. Confirmarea primirii unui anume drept ii este adusa la cunostinta clientului prin intermediul unei modificari a unui control grafic. Clientul poate decide pastrarea sau cedarea controlului intr-o masura limitata de catre server. Aceasta decizie este necesara pentru a pastra un anumit grad de echitate in ceea ce priveste alocarea drepturilor si durata acestei alocari.
In ceea ce priveste continutul executabil, acesta poate fi incarcat pe masina client atunci cind este disponibila adresa acestuia, lucru vizibil prin schimbarea informatiei unui control grafic.
Pentru inregistrare la grupul de discutii, transmitere de cereri si de informatii catre manager, clientii utilizeaza apeluri CORBA obisnuite. Pentru difuzarea evenimentelor, insa, acestia utilizeaza un canal de comunicatie. Acest canal este utilizat si de manager pentru a transmite comenzi si informatii catre clienti.
Gestiunea clientilor conectati, la un moment dat, este tinuta de server intr-o baza de date.
La abandonarea discutiei, clientul anunta acest lucru server-ului. Din cind in cind, server-ul verifica daca un client cu care nu a mai comunicat un anume timp, mai este activ. In caz ca nu primeste raspuns, clientul respectiv este scos din baza de date. Un client care este in drept sa comunice text sau imagine trebuie, mai intii, sa cedeze acest drept, sau sa-i fie prelevat acest drept, inainte de a putea parasi grupul de discutii.
Server-ul primeste cereri de servicii de la clienti. In caz ca serviciul respectiv este disponibil, ele este oferit pe loc clientului. In caz contrar, clientul este inregistrat intr-o coada de asteptare, evoluind in cadrul acesteia in functie de o anumita politica. Clientul nu are posibilitatea sa mai faca o cerere pentru acelasi serviciu in caz ca cererea anterioara nu a fost acceptata. Solicitarea unui client de a renunta la conexiunea cu server-ul duce, in mod automat, la scoaterea primului din toate cozile de asteptare asociate diverselor servicii solicitate anterior.
Clientii se pot decide asupra unuia sau mai multor modalitati de comunicare dintre cele trei care le stau la dispozitie.
Continutul executabil poate fi preluat de la o adresa de Web pe care o furnizeaza un client ce doreste sa faca o anumita demonstratie. Orice client poate face acest lucru, in orice moment; cu toate acestea, este introdusa o pauza de siguranta, dupa ce adresa URL devine disponibila in fereastra, pentru a asigura faptul ca cei interesati de continutul executabil au timp sa actioneze controlul ce declanseaza incarcarea programului inainte ca o alta, eventuala, adresa URL sa fie afisata.
Sectiunea chat reprezinta modalitatea prin care se pot schimba informatii sub forma de text. Textul este editat si apoi difuzat si celorlalti participanti, fara interventia managerului, prin intermediul unui ansamblu de obiecte de tip special ce se constituie intr-un un canal de comunicatie si este descris intr-o sectiune urmatoare.
Sectiunea board are drept scop circulatia informatiilor de tip grafic. Aceasta se refera, concret, la desenarea pe o suprafata special destinata acestui scop, utilizind mouse-ul. Suprafata de desenare are inregistrate handler-e de evenimente. Evenimentele sint prelucrate prin mecanisme specifice si apoi sint trimise si celorlalti participanti interesati, prin intermediul canalului de comunicatie. Pe masinile acestora are loc procesul invers, astfel incit evenimentele produse pe masina emitatoare sa se reflecte intocmai pe masina receptoare.
Ca si in cazul modulului chat, comunicarea este incheiata fie de catre client, fie de catre manager, in modul particular descris mai sus.
Receptia evenimentelor prin intermediul controalelor grafice este decuplata de transmiterea informatiei, mai departe, prin canalul de comunicatie. Acest lucru se realizeaza prin intermediul unor buffer-e locale, la care au acces prioritar procesele ce preiau evenimente, fata de cele ce prelucreaza informatia si o transmit mai departe.
Sectiunea demo va fi destinata incarcarii, pe masina locala a unui fisier avind continut executabil. Acest fisier poate fi furnizat de oricare participant la discutii, si reprezinta adresa unui fisier de pe Web.
Grupul de discutii este initiat de catre administratorul masinii server. Modulul server nu trebuie sa fie, neaparat, instalat pe masina initiatorului. Pe de alta parte, toti participantii la grupul de discutii trebuie sa se afle in posesia pachetului de programe necesar participarii la discutii.
Pe masina coordonatorului discutiilor vor fi disponibile informatii cu privire la numarul de participanti la momentul respectiv si diferite statistici necesare administrarii sistemului. Managerul dispune de mecanisme ce permit transmiterea de comenzi catre clienti. El utilizeaza aceste mecanisme prin intermediul unor controale dispuse pe interfata grafica cu care este prevazut modulul de comanda.
4.2 Implementarea propriu-zisa
4.2.1 Prezentarea interfetei client
Interfata prezentata clientului se doreste a fi o entitate ergonomica, ce ofera clientului cai simple de rezolvare a diverselor cerinte prin intermediul controalelor grafice. Pentru a mari simplitatea utilizarii si a reduce la maxim probabilitatea introducerii comenzilor eronate, controalele ce pot fi actionate de catre utilizator devin active doar in cazul in care reprezinta optiuni viabile in acel context. Astfel, un buton nu poate fi actionat decit in cazul in care clientul este indreptatit sa faca acest lucru si daca actiunea ce se declanseaza la apasarea butonului poate avea loc in acel context si la acel moment de timp.
Pentru implementarea interfetei s-au folosit clase din pachetele borland.jbcl si java.awt.
Java Beans Component Library (JBCL) reprezinta o structura API (Application Programming Interface) pentru dezvoltarea aplicatiilor Java. JBCL este construit pe baza arhitecturii com.sun.java.swing. Toate componentele JBCL au ca radacina JComponent, ceea ce le confera transparenta, variate atribute si facilitati si aspect si comportament diverse. JBCL permite mapare de textura pe toate componentele si este de tip “lightweight”, ceea ce implica neutilizarea ferestrelor native si nesolicitarea excesiva a sistemului de operare datorata cererilor suplimentare adresate acestuia de catre componentele peer. Toate componentele sint serializabile.
Suprafata pe care sint dispuse controalele grafice este o instanta a clasei DecoratedFrame. Aceasta reprezinta o fereastra top-level ce dispune de titlu, bara de menu si controale ale ferestrei.
Aceasta suprafata poate fi vazuta ca fiind divizata in patru sectiuni:
Sectiunea ce permite conectarea si deconectarea clientului la si dela grupul de discutii.
La lansarea in executie a programului, clientului i se prezinta o interfata ale carei controale active sint numai cel ce permite clientului sa se inregistreze ca participant la grupul de discutii, etichetat Register, si cel care determina parasirea aplicatiei, etichetat Step Back. Odata inregistrat, clientul poate interactiona si cu alte controale care ii vor inlesni obtinerea diverselor servicii. Dupa conectarea la grupul de discutii, butonul Register devine inactiv, considerind ca nu are sens o a doua inregistrare a clientului, pe cind butonul Step Back ramine activ, permitind clientului sa paraseasca in orice moment grupul de discutii.
Cele doua butoane sint integrate intr-o structura mai complexa. Toate componentele acestei structuri provin din pachetul borland.jbcl.control, cu exceptia obiectelor apartinind pachetului borland.jbcl.layout. La baza structurii se afla un obiect de tip SplitPanel care reprezinta un container compartimentat. Sectiunile acestuia sint controlate implicit, dpdv al dimensiunilor si al pozitionarii, de catre un manager de layout, instanta a clasei PaneLayout, ce face parte din pachetul amintit mai sus. Acest container este divizat in doua parti de catre doua obiecte GroupBox, ce reprezinta containere cu titlu. Ele au asociate, ca manager de layout, cite un obiect din clasa XYLayout, ce permite plasarea, in orice pozitie, a unui obiect in cadrul containerului. Containerul init contine butonul Register, iar containerul finish contine butonul Step Back, ambele fiind instante ale clasei ButtonControl.
Sectiunea chat are in componenta o subsectiune ce reprezinta zona de editare insasi, precum si subsectiunea de control.
Introducerea textului se face intr-o fereastra reprezentata de o instanta a clasei TextAreaControl. Aceasta fereastra se afla continuta intr-un container GroupBox, dotat cu un manager de layout de tip XYLayout.
Subsectiunea de control cuprinde doua butoane din clasa ButtonControl. Este vorba de butonul Speak, ce are rolul de a solicita managerului dreptul de a transmite informatie sub forma de text, si butonul Over, la actionarea caruia managerul este anuntat de incheierea actiunii anterioare si poate ceda acest drept altui client. Cele doua butoane se afla continute intr-un container de tip SplitPanel. Atit timp cit clientul nu si-a exprimat intentia de a introduce text, butonul Speak ramine activ, iar butonul Over – inactiv. Dupa ce clientul a actionat butonul Speak, situatia se inverseaza; Speak devine inactiv deoarece cererea clientului a fost inregistrata si va fi servita atunci cind managerul va decide. Pina la primirea comenzii, butonul Over, desi activ, nu produce nici un efect la actionare. El decide terminarea introducerii textului, deci implica detinerea acestui drept, apriori, de catre client. La cedarea controlului, decisa de catre client sau manager, situatia revine la starea de la inceput, cu butonul Speak activ si butonul Over inactiv.
Sectiunea board este utilizata de catre client pentru a desena liber cu ajutorul mouse-ului. Ca si sectiunea chat, si aceasta este constituita din doua subsectiuni – cea de desenat, propriu zisa, si cea de control.
Desenarea se realizeaza pe o suprafata ce reprezinta o instanta a unui obiect utilizator, instanta a clasei CanvasBean. Acest obiect se afla continut intr-un container de tip GroupBox, dotat cu un manager de layout de tip XYLayout.
Clasa CanvasBean extinde clasa java.awt.Canvas si reprezinta un element de tip JavaBean, construit special pentru situatia concreta din program. Un extras de cod este prezentat mai jos:
public class CanvasBean extends Canvas implements
MouseListener, MouseMotionListener, KeyListener {
int x, y, x1, y1;
final int xd = 1, yd = 10;
public CanvasBean() {}
// effectively draw on the board
public void paint(Graphics g) {
g.drawLine(x, y, x1, y1);
}
// print current drawer on client’s board
public void writeDrawer(String name) {
Graphics g = getGraphics();
g.drawString(name+":", xd, yd);
}
// initiate drawing process
public void drawOnCanvas(int x, int y, int x1, int y1) {
this.x = x;
this.y = y;
this.x1 = x1;
this.y1 = y1;
repaint();
}
public void update(Graphics g) {
paint(g);
}
}
Atit sectiunea chat, cit si sectiunea board se afla continute intr-un obiect de tip SplitPanel.
Cele doua sectiuni au asociate informatii de stare ce sint afisate intr-o fereastra compartimentata. In fapt, fiecarei sectiuni ii corespunde un obiect de clasa java.awt.TextField – un cimp in care poate fi afisat text. Un container compartimentat de tip SplitPanel contine cele doua obiecte TextField, iar acest container este continut intr-un container cu titlu, de tip GroupBox, avind numele Status. Informatiile din cele doua cimpuri se refera la permisiunile de introducere de text sau, respectiv, desenare, din momentul respectiv.
Politica utilizata pentru activarea si dezactivarea butoanelor este asemanatoare cu cea de la sectiunea chat.
In sfirsit, cea de-a patra sectiune se ocupa de informatiile asociate continutului executabil.
Exista doua cimpuri in care se poate introduce text, reprezentate de doua obiecte ale clasei TextField. Primul este utilizat de catre client pentru a introduce o adresa URL ce va fi difuzata tuturor celorlalti clienti. Cel de-al doilea cimp serveste la afisarea adresei primita in urma actiunii descrise mai sus. Cele doua cimpuri au asociate, respectiv, cite un buton, instante ale clasei ButtonControl. Prin actionarea butonului Deliver URL, adresa continuta de cimpul urlAddress se va difuza celorlalti clienti. Odata afisata o adresa URL in cimpul urlDemo, clientul poate decide, prin actionarea butonului Accept Demo, incarcarea unui continut executabil de la adresa continuta in cimpul amintit mai sus. Incarcarea acestui continut, in fapt, o clasa Java, are drept rezultat rularea programului respectiv pe masina clientului.
Trebuie mentionat ca, spre deosebire de butonul Deliver URL, ce ramine continuu activ, butonul Accept Demo devine activ doar dupa ce o adresa URL devine disponibila in cimpul urlAddress.
Cele patru entitati descrise anterior se afla continute intr-un container de tip SplitPanel. La rindul sau, acesta este continut intr-un obiect GroupBox, avind numele Demo Section, si, asociat, un layout manager de tip PaneLayout..
In continuare sint prezentate doua capturi de ecran, surprinse in timpul executiei programului client:
Fig. 5 – Starea interfetei inainte de inregistrarea clientului
Fig. 6: Starea interfetei in timpul rularii programului
4.2.2 Prezentarea interfetei manager
Interfata manager este utilizata de catre persoana care a initiat grupul de discutii si care poate interveni activ in desfasurarea evenimentelor. In cadrul acestei interfete se regasesc informatii asupra numarului de clienti inregistrati la grupul de discutii, numarul clientilor din cozile de asteptare pentru servicii, date temporale diverse precum si posibilitatea de a preleva drepturi prin intermediul actionarii unor controale.
Interfata poate fi impartita in doua subsectiuni:
– de decizie;
– de informare.
Pentru implementarea interfetei s-au folosit clase din pachetele borland.jbcl si java.awt.
4.2.2.1 Subsectiunea de decizie
Subsectiunea de decizie este gindita in ideea ca anumiti clienti pot abuza de anumite drepturi care le-au fost acordate. Aceste drepturi pot fi, deci, prelevate.
Aceasta subsectiune are la baza un obiect de tipul SplitPanel. In cadrul acestuia se regasesc elemente dispuse astfel incit sa constituie o structura tabelara. Capul de tabel este alcatuit din trei obiecte apartinind clasei LabelControl. Sub primele doua etichete se afla cite doua cimpuri pentru afisarea informatiei sub forma de text, implementate ca obiecte apartinind clasei TextFieldControl. Sub cea de-a treia eticheta se afla dispuse doua butoane, implementate ca obiecte ale clasei ButtonControl. Primele doua rubrici imbraca urmatoarele semnificatii:
Performer – reprezinta numele clientului care detine dreptul sa introduca informatie sub forma de text, respectiv, sub forma grafica.
On since – reprezinta momentul de timp la care clientul respectiv a primit acest drept, sau, in cazul in care rubrica din stinga este vida, momentul de timp la care ultimul client care a detinut dreptul respectiv a incheiat transmiterea de informatii.
Ce-a de-a treia rubrica, Stop Performer, contine doua butoane, Stop Speaker si Stop Drawer, care au rolul de a preleva drepturile celor doi clienti, respectiv, la decizia managerului.
4.2.2.2 Subsectiunea de informare
Subsectiunea de informare cuprinde informatii cu privire la numarul de clienti inregistrati la grupul de discutii, numarul de clienti ce se afla in coada de asteptare pentru dobindirea dreptului de a difuza text si numarul de clienti din coada de asteptare pentru dreptul de a distribui informatie cu continut grafic.
Fiecare din aceste trei informatii se prezinta sub forma unei perechi de obiecte LabelControl – TextFieldControl. Cele trei perechi se afla dispuse in cadrul unui container de tip SplitPanel.
De asemenea, este furnizata si data lansarii in executie a grupului de discutii. Aceasta este afisata in cadrul unei ferestre de tip TextFieldControl si este insotita de un text implementata ca instanta a clasei LabelControl. Acest ansamblu se afla continut intr-un container, instanta a clasei BevelPanel, container ce este controlat de catre un manager de layout, instanta a clasei PanelLayout.
Mai trebuie mentionat faptul ca toate controalele descrise mai sus se afla dispuse intr-o fereastra implementata ca un obiect apartinind clasei DecoratedFrame, insotit de un manager de layout din clasa XYLayout.
In continuare sint prezentate doua instante ale interfetei manager:
Fig. 7 – Interfata managerului la pornirea programului
Fig. 8 – Interfata manager in timpul desfasurarii aplicatiei
4.3 Comunicarea
Cheia de bolta a acestui proiect o reprezinta mecanismul de comunicare. Aceasta afirmatie se refera nu atit la transmiterea comenzilor si a informatiilor de stare intre diversele entitati comunicante, ci, in special, la modul in care trebuie sa evolueze fluxurile de evenimente ce se petrec pe masinile locale si trebuie difuzate tuturor celorlalti clienti interesati.
4.3.1 Comenzi si informatii. Evenimente
Comenzile si informatiile se transmit intre manager si clienti. Prin intermediul comenzilor, managerul aduce la cunostinta clientilor primirea unui anume drept sau incetarea detinerii acestuia. Pe de alta parte, in cazul in care nu-i este destinata, comanda are rol de informatie, permitind clientului sa cunoasca anumite informatii despre ceilalti clienti, si anume cine detine un anume drept la momentul respectiv, aceste detalii fiind necesare tuturor clientilor pentru a fi afisate in cadrul interfetei grafice. In acelasi timp, clientii isi pot transmite reciproc anumite informatii. In particular, toti clientii, mai putin emitatorul, sint adusi in situatia sa cunoasca adresa URL a continutului executabil pe care emitatorul il pune la dispozitie la un moment dat, prin difuzarea acestei adrese si afisarea ei in cimpul special destinat acestui scop, in cadrul interfetei grafice. Informatiile trimise de catre clienti, managerului, se refera la inregistrarea unui solicitant in grupul de discutii, ca prima actiune ce trebuie initiata de catre client, la solicitarea dreptului de a transmite informatie sub forma de text sau de tip grafic, la cedarea unuia din aceste drepturi, precum si la anuntarea managerului cu privire la parasirea grupului de discutii.
Comunicarea in sensul client – manager are loc prin intermediul apelurilor de metode ale obiectelor CORBA. Metodele sint puse la dispozitie de catre manager, acesta fiind inregistrat ca obiect CORBA la ORB (Object Request Broker).
Evenimentele se produc ca urmare a interactiunii clientului cu controalele dispuse pe interfata grafica a acestuia. Evenimentele provin de la suprafata destinata introducerii de text (zona chat) si de la suprafata pe care se poate desena cu mouse-ul (zona board).
Evenimentele produse la nivelul sistemului de operare, datorate actionarii tastaturii si mouse-ului, sint preluate de catre JVM (Java Virtual Machine) si transferate obiectelor ce s-au inregistrat ca ascultatori de evenimente (EventListener) pentru controalele cu care interactioneaza clientul.
Initial, evenimentele captate de catre EventListener erau prelucrate in cadrul metodelor asociate si transmise managerului, prin intermediul apelurilor CORBA, pentru a fi difuzate si celorlalti clienti. Aceasta solutie s-a dovedit a nu fi optima, deoarece activitatea de preluare a evenimentelor generate de actionarea mouse-ului, de catre JVM, are loc cu o frecventa ce depinde de incarcarea JVM. La o incarcare mai mare sint preluate mai putine evenimente in unitatea de timp. Astfel, se putea constata o pierdere de informatie inacceptabila si sesizabila, la care se adauga si o scadere considerabila de viteza a programului.
A aparut, astfel, ideea decuplarii sectiunii in care se preiau evenimente de sectiunea in care acestea sint prelucrate si trimise managerului.
A fost abordata, mai intii, sectiunea chat. Lucrurile decurgeau in modul urmator:
la nivelul sistemului de operare se produceau evenimente legate de introducerea textului in fereastra de editare, evenimente preluate apoi de catre JVM si distribuite obiectului de tip KeyAdapter sub forma de evenimente KeyEvent. Caracterul asociattastei apasate era adaugat unui buffer local. Secventa de cod era urmatoarea:
void chatArea_keyPressed(KeyEvent e) {
char c = e.getKeyChar();
// append typed chars to existing string
if (c != e.CHAR_UNDEFINED)
synchronized(this) {
text.append(c);
}
}
Buffer-ul text avea rolul de a decupla cele doua sectiuni descrise mai sus, fiecare evoluind in mod independent. Buffer-ul utilizat se considera a fi suficient de mare pentru a putea suplini o eventuala diferenta de viteza intre cele doua sectiuni.
In cea de-a doua sectiune, buffer-ul era golit iar textul preluat era transmis managerului pentru a fi difuzat celorlalti clienti. Iata secventa de cod:
synchronized(this) {
if (text.length() != 0) {
// broadcast text
tm.putString(text.toString());
// reinitialize local text buffer
text = new StringBuffer();
}
}
Metoda putString (String) avea rolul de a transmite textul, managerului. Dupa ce acest lucru era realizat, buffer-ul era reinitializat. Sincronizarea era realizata pentru a evita interferenta celor doua sectiuni in contextul accesarii aceleiasi variabile.
La nivelul managerului, fiecare client era inregistrat intr-o tabela Hash. Un element al tabelei avea o cheie asociata, reprezentind numele clientului, si o informatie constind in sirul de caractere destinat respectivului client si inca nepreluat de catre acesta. Metoda putString() parcurgea tabela Hash si adauga, pentru fiecare client, noua informatie la cea veche, concatenind, practic, cele doua siruri, dupa cum se vede mai jos:
public void putString(String txt) {
String s = null;
if (!txt.equals("")) {
// appends new text to the old ones
Enumeration k = pool.keys();
Enumeration o = pool.elements();
for (;k.hasMoreElements();) {
s = (String)k.nextElement();
if (!s.equals(speaker))
pool.put(s, ((StringBuffer)o.nextElement()).append(txt));
else
o.nextElement();
}
}
else
speaker = l.retrieve();
}
In secventa de cod anterioara, pool reprezinta tabela Hash, iar speaker – clientul ce detinea controlul sectiunii chat la momentul respectiv, si caruia nu trebuia sa I se distribuie noua informatie, deoarece aceasta era afisata local.
– Informatia stocata astfel era preluata de catre ceilalti clienti prin intermediul unei metode CORBA, getString(). Aceasta metoda era apelata de catre toti clientii, cu exceptia celui ce detinea controlul chat, din interiorul unui ciclu while. Codul se prezenta astfel:
public String getString(String name, BooleanHolder bh) {
bh.value = name.equals(speaker) ? true : false;
return ((StringBuffer)pool.put(name, new StringBuffer())).toString();
}
Rezultatul apelului metodei era noul text. Buffer-ul asociat clientului in tabela Hash era actualizat.
Aceasta metoda, pe linga rolul de a transmite noul text clientului, mai era utilizata si la comunicarea obtinerii dreptului de a transmite text. Acest lucru se realiza prin intermediul obiectului de tip BooleanHolder, un recipient de valoare de tip boolean ce reprezinta maparea unei astfel variabile cu atributul out, din IDL CORBA in Java.
Clientul apela aceasta metoda atit timp cit nu detinea dreptul de a transmite text el insusi. Iata cum era utilizata metoda pe masina client (extras de cod):
do {
// write down obtained string
String ss = tm.getString(name, bh);
if (!ss.equals(""))
chatArea.append(ss);
} while (!bh.value);
Metoda de comunicare prezentata anterior prezenta mai multe neajunsuri, din care se pot mentiona:
Obligativitatea clientului de a apela ciclic metoda getString() pentru a obtine noul text; acest lucru era necesar deoarece nu exista nici o modalitate prin care clientul sa fie anuntat la aparitia unei schmbari, de catre manager. Acest apel ciclic se traducea si intr-o incarcare nejustificata a masinii manager, deoarece metoda era executata pe serverul de obiecte CORBA, adica managerul.
Depozitarea, chiar si numai temporara a sirurilor de caractere la manager. Aceste siruri puteau duce la un consum nejustificat si important de memorie, in cazul unei numar mare de clienti sau a unor diferente semnificative de viteza intre masini.
Abordarea primitiva a problemei, tinind seama de serviciile oferite de implementarile CORBA existente.
Extindere extrem de dificila si ineficienta a metodei pentru cazul transmiterii de evenimente generate de miscarea mouse-ului pe suprafata de desenare.
Motivele expuse mai sus, ca si alte considerente, au condus la ideea cautarii unui mecanism mai evoluat de comunicare. Dupa cum s-a mentionat si in sectiunile anterioare, au fost examinate mai multe solutii. Tinind seama de specificul aplicatiei, in care exista, la un moment dat, entitati care produc informatie si entitati care consuma acea informatie, a rezultat concluzia ca un mecanism de comunicare adecvat ar putea evolua dupa paradigma producator – consumator. Astfel, producatorul ar putea construi unitati de informatie, intr-un ritm propriu, iar consumatorii ar trebui anuntati de producerea acestor evenimente si le-ar putea prelua intr-un ritm, de asemenea, propriu.
4.3.2 Canalul de Evenimente
Standardul CORBA ofera mai multe solutii acestei probleme. Din pacate, implementarile CORBA disponibile nu prezinta implementari pentru toate solutiile oferite de standard.
Evaluind posibilitatile oferite de implementarile ORB in Java, disponibile prin intermediul Internet, s-a evidentiat o noua varianta de ORB a firmei Visigenic, si anume 3.3, aparuta in acest an. Aceasta, spre deosebire de cea din 1998, integrata in mediul de dezvoltare Jbuilder 2.0, se recomanda prin noutatea reprezentata de pachetul Event Service.
Pachetul Event Service permite decuplarea comunicarii dintre obiecte. Pentru a realiza acest lucru, este utilizat modelul producator-consumator, ce permite ca mai multe obiecte, denumite producatori, sa trimita date in mod asincron catre alte obiecte, denumite consumatori, prin intermediul unui mediu de comunicare, numit canal.
4.3.2.1 Proxy consumers si proxy suppliers
Consumatorii si producatorii sint complet decuplati unii de altii prin intermediul unor obiecte proxy. In loc sa interactioneze unul cu celalalt, fiecare in parte obtine un obiect proxy de la un obiect de tip EventChannel, reprezentind canalul de comunicare. Obiectele de tip producator obtin un proxy consumator iar cele de tip consumator obtin un obiect proxy producator.
Obiectul EventChannel este atit consumator, cit si producator de evenimente. Datele ce se transfera intre producatori si consumatori sint reprezentate de obiecte Any, permitind oricarui obiect CORBA sa fie transmis intr-o maniera sigura. Obiectele de tip producator si consumator comunica prin intermediul canalului de evenimente utilizind apeluri CORBA standard.
Serviciul de evenimente VisiBroker este conform cu specificatiile OMG, cu doua exceptii:
Acest serviciu suporta doar evenimente generice. In momentul de fata nu exista evenimente cu tip in cadrul acestui serviciu;
Nu este oferita confirmarea primirii datelor, nici din partea canalului de evenimente, nici din partea obiectelor consumatoare. Protocolul utilizat pentru transmisie este TCP/IP, acest lucru determinind corectitudinea si siguranta transmisiei.
4.3.2.2 Modele de comunicare
Serviciul de evenimente furnizeaza doua modele pentru comunicatie: push si pull. In modelul push, obiectele de tip producator asigura controlul fluxului de date prin transmiterea acestora catre consumatori. In celalalt model, obiectele de tip consumator asigura controlul fluxului, prin receptia datelor de la producatori.
Canalul de evenimente izoleaza producatorii si consumatorii, acestia nestiind ce tip de model este utilizat in partea opusa a canalului. Acest lucru inseamna ca un producator de tip pull poate furniza date catre un consumator push, iar un producator de tip push poate transmite date catre un consumator de tip pull.
Canalul de evenimente poate fi implementat atit ca obiect de sine statator, cit si ca parte a procesului producator. In acest din urma caz, avem de-a face cu un In-process event channel. Obiectele vehiculate prin canal sint pastrate intr-o coada. Dimensiunea acesteia se poate seta, astfel incit sa se evite blocarea unora dintre partenerii de conversatie mai lenti.
Cel mai comun dintre cele doua modele este push. Obiectul de tip producator trimite date catre obiectul proxy consumator. Obiectul consumator cicleaza in astaptarea datelor. Canalul de evenimente faciliteaza transferul datelor. In celalalt model, canalul extrage date de la obiectul producator si le pune la dispozitia obiectului consumator. Producatorul cicleaza in asteptarea cererilor.
In modelul pull, canalul de evenimente extrage in mod regulat date de la un obiect producator, le introduce intr-o coada, ele devenind astfel disponibile pentru a fi extrase de catre un obiect consumator. Producatorul de tip pull isi petrece majoritatea timpului intr-o bucla, fiind in asteptarea cererilor de la ProxyPullConsumer. Consumatorul pull solicita date de la ProxyPullSupplier atunci cid este gata pentru acest lucru. Canalul de evenimente solicita date de la producatorul pull si le introduce intr-o coada, punindu-le, astfel, la dispozitia obiectului
4.3.2.3 Utilizarea canalelor de evenimente
Cind este creat un obiect EventChannel, la acesta nu sint conectati nici producatori, nici consumatori. Un obiect producator sau consumator se conecteaza si utilizeaza canalul de evenimente parcurgind urmatoarele etape:
Se conecteaza la obiectul EventChannel;
Obtine un obiect de administrare de la EventChannel si il utilizeaza pentru a obtine un obiect proxy;
Se conecteaza la obiectul proxy;
Incepe sa transmita sau sa receptioneze date.
Metodele utilizate pentru implementarea pasilor enumerati mai sus variaza, in functie de tipul obiectului (producator sau consumator) si de modelul de comunicatie adoptat.
In cazul de fata va fi utilizat modelul push, atit pentru producatori cit si pentru consumatori. In continuare este prezentat schematic acest model:
Fig. 9 – Modelul Push Consumer
Interfetele IDL CORBA pentru obiectele utilizate se prezinta in modul urmator:
Interfata obiectului PushSupplier:
module CosEventComm {
interface PushSupplier {
void disconnect_push_supplier();
};
};
Metoda disconnect_push_supplier este apelata de catre EventChannel pentru a deconecta producatorul, atunci cind canalul de evenimente este distrus.
Interfata obiectului PushConsumer:
module CosEventComm {
exception Disconnected();
interface PushConsumer {
void push(in any data) raises(Disconnected);
void disconnect_push_consumer();
};
};
Metoda push primeste un parametru de tip Any si il converteste la un tip de date adecvat. Metoda disconnect_push_supplier este apelata de catre EventChannel pentru a deconecta consumatorul atunci cind canalul de evenimente este distrus.
Interfata obiectului ConsumerAdmin:
module CosEventChannelAdmin {
interface ConsumerAdmin {
ProxyPushConsumer obtain_push_supplier();
ProxyPullConsumer obtain_pull_supplier();
};
};
Aceasta interfata este utilizata de catre aplicatiile consumator pentru a obtine o referinta la un obiect proxy producator. Acesta reprezinta cel de-al doilea pas ce trebuie facut pentru a conecta o aplicatie consumator la un canal de evenimente.
Metoda obtain_push_supplier este utilizata in cazul in care aplicatia consumator respecta modelul push. In cazul modelului pull, va fi utilizata metoda obtain_pull_supplier. Referinta returnata de metoda este utilizata fie pentru a apela metoda connect_push_consumer, fie metoda connect_pull_consumer, in functie de modelul de comunicare adoptat.
Interfata obiectului SupplierAdmin:
module CosEventChannelAdmin {
interface SupplierAdmin {
ProxyPushConsumer obtain_push_consumer();
ProxyPullConsumer obtain_pull_consumer();
};
};
Aceasta interfata este utilizata pentru a obtine o referinta la obiectul proxy consumator. Acesta reprezinta cel de-al doilea pas in conectarea unei aplicatii producator la EventChannel.
Daca aplicatia respecta modelul push, atunci este utilizata metoda obtain_push_consumer, in caz contrar fiind apelata metoda obtain_pull_consumer.
Interfata obiectului ProxyPushConsumer:
module CosEventChannelAdmin {
exception AlreadyConnected();
interface ProxyPullConsumer : CosEventComm::PullConsumer {
void connect_pull_supplier(
in CosEventComm::PullSupplier pull_supplier)
raises(AlreadyConnected);
};
};
Aceasta interfata este utilizata de catre un producator push. Metoda connect_push_supplier este utilizata de catre obiectul PushSupplier pentru a se conecta la EventChannel. Daca se incearca o a doua conectare la acelasi proxy, se va genera o eaceptie AlreadyConnected.
Interfata obiectului ProxyPushSupplier:
module CosEventChannelAdmin {
exception AlreadyConnected();
interface ProxyPushSupplier : CosEventComm::PushSupplier {
void connect_push_consumer(
in CosEventComm::PushConsumer push_consumer)
raises(AlreadyConnected);
};
};
Aceasta interfata este utilizata de catre o aplicatie push consumator. Este furnizata metoda connect_push_consumer, necesara obiectului PushConsumer pentru a se conecta la EventChannel. Daca se incearca o a doua conectare la acelasi obiect se va genera o exceptie AlreadyConnected.
Obiectul EventChannel pune la dispozitie metodele necesare pentru adaugarea de producatori si consumatori si pentru distrugerea canalului de evenimente.
Atit producatorii, cit si consumatorii utilizeaza metoda bind pentru a obtine o referinta la un obiect EventChannel. Ca in orice caz de invocare a metodei bind, se poate specifica numele obiectului invocat si eventuale optiuni. Daca numele obiectului nu este specificat, cade in sarcina ORB-ului de a decide ce obiect va fi utilizat.
Trebuie tinut seama si de faptul ca, in anumite medii, se poate intimpla ca aplicatiile consumator sa ruleze mai lent decit aplicatiile producator. Variabila MAX_QUEUE_LENGTH poate fi setata astfel incit sa se evite sufocarea consumatorului cu mesaje. Fiecare consumator dispune de o coada separata, dar, odata setat parametrul anterior pentru canalul de evenimente, toti consumatorii vor opera cu cozi de aceeasi lungime. In caz ca nu se specifica o anumita valoare, coada va avea lungimea 100.
Solutia prezentata nu elimina intrutotul dezavantajele anterioare. Verificarea ciclica a existentei informatiei disponibile nu este inlaturata, dar, in noile conditii, este realizata de catre obiectul proxy consumer, degrevind, in acest mod, masina client de aceasta sarcina. Transferul evenimentelor se realizeaza prin intermediul canalului de comunicatie, managerul revenindu-i numai rolul de comanda si coordonare a transferului. Dupa cum am mentionat, mecanismul permite o decuplare a entitatii producatoare de cele consumatoare, fiecare putind evolua intr-un ritm propriu. Cu toate acestea, cade in sarcina managerului de a ajusta dimensiunea cozii astfel incit sa poata fi realizat un transfer sigur si fara pierderi de informatie.
Tinind seama de caracteristicile acestui mecanism, a aparut, in prima faza, ideea adoptarii unei solutii unitare. Acest lucru implica o comunicare exclusiva prin intermediul canalului de evenimente.
Implementarea prevedea existenta a doua canale de comunicatie, unul destinat exclusiv comenzilor si informatiilor de stare, iar celalalt – evenimentelor. A fost realizata implementarea completa a acestei solutii, urmind sa fie testate in conditii reale. Din pacate, odata instalat si pornit acest sistem, s-a constatat ca, desi canalele de comunicatii erau identice, functiona, efectiv, doar unul singur. In cazul canalului care nu functiona, s-a constatat ca disfunctia aparea in cadrul comunicarii dintre obiectele proxy. Mai precis, exista un blocaj care impiedica transmiterea obiectelor Any, preluate de catre ProxyPushConsumer de la PushConsumer, catre ProxyPushSupplier. Nu s-a putut determina cauza aparitiei acestei disfunctionalitati, ea fiind datorata implementarii firmei Visigenic, sursele nefiind disponibile. Tinind seama de virsta frageda a acestei implementari, precum si faptul ca a fost declarata versiune de incercare, disponibila gratuit prin Internet, se poate considera ca mecanismul nu are, inca, maturitatea necesara pentru a fi utilizat in aplicatii ce se doresc a fi sigure si fiabile. Cu toate acestea, in contextul acestui program si tinind seama ca lucrurile, in ceea ce priveste dezvoltarea ulterioara a mecanismului, nu pot evolua decit intr-o directie pozitiva, s-a considerat ca se poate pastra aceasta solutie, utilizata, insa, cu precautia si resrictiile de rigoare.
Implementarea finala a devenit una mixta. Anume, comunicarea in sensul clienti -> manager are loc prin intermediul apelurilor de metode CORBA clasice, pe cind comunicarea in sensurile manager -> clienti si client -> clienti are loc prin intermediul unui canal de comunicatie.
4.4 Entitati ce compun sistemul
O entitate componenta a sistemului consista intr-un grup de obiecte. Se contureaza, astfel, trei entitati:
Client – reprezinta un participant la grupul de discutii; este un solicitant de servicii.
Manager – reprezinta entitatea cu rol de coordonare si comanda; permite clientilor conectarea la grupul de discutii si supervizeaza desfasurarea actiunilor din sistem, intervenind activ prin receptionarea cererilor clientilor, acordarea si prelevarea de drepturi si transmiterea de informatie.
Canalul de comunicatie – reprezinta modalitatea prin care se transmit toate evenimentele produse ca urmare a interactiunilor dintre clienti si controalele ce permit introducerea de informatie sub forma de text sau grafica, precum si a comenzilor si informatiilor furnizate de catre manager sau de catre clienti si avind drept destinatie clientii.
4.4.1 Clienti
Ansamblul elementelor ce formeaza pachetul Client este constituit din urmatoarele clase:
PartyLine – reprezinta clasa ce contine punctul de inceput al programului. Contine, evident, metoda main, in cadrul careia este construit un manager de securitate, obiect al clasei com.sun.java.swing.UIManager. Acest obiect are rolul de a se ocupa de aspectul si comportamentul (look and feel) unei clase ce reprezinta o interfata grafica. In cazul de fata, se poate seta una din urmatoarele trei solutii implicite, proprietati ale componentelor JavaBeans:
UIManager.setLookAndFeel(new com.sun.java.swing.plaf.windows.WindowsLookAndFeel());
UIManager.setLookAndFeel(new com.sun.java.swing.plaf.motif.MotifLookAndFeel());
UIManager.setLookAndFeel(new com.sun.java.swing.plaf.metal.MetalLookAndFeel());
Cele trei solutii seteaza proprietatile interfetei client in conformitate cu specificatiile Windows, Motif respectiv Metal.
Ultima instructiune din metoda main este apelul constructorului acestei clase. La executia acestuia, se construieste o instanta a clasei PartyLineFrame, care, printre altele, construieste efectiv interfata client.
De asemenea, in cadrul metodei PartyLine se stabileste locul unde va fi afisata interfata client pe ecranul monitorului si se stabilesc alte citeva caracteristici ale acestei interfete in legatura cu dimensiunile sale si cu modul de afisare. Codul de initializare este prezentat mai jos:
public PartyLine() {
PartyLineFrame frame = new PartyLineFrame();
//Validate frames that have preset sizes
//Pack frames that have useful preferred size info, e.g. //from their layout
if (packFrame)
frame.pack();
else
frame.validate();
//Center the window
Dimension screenSize
Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height)
frameSize.height = screenSize.height;
if (frameSize.width > screenSize.width)
frameSize.width = screenSize.width;
frame.setLocation((screenSize.width – frameSize.width) / 2, (screenSize.height – frameSize.height) / 2);
frame.setVisible(true);
}
PartyLineFrame – reprezinta clasa principala a pachetului Client. Extinde clasa DecoratedFrame din pachetul borland.jbcl.control. Functionalitatile acestei clase sint multiple:
Initializarea interfetei grafice
Acest lucru se realizeaza la apelul metodei jbInit, din cadrul constructorului. Ca urmare a acestui apel se construiesc toate elementele grafice ale interfetei si se seteaza proprietatile lor. De asemenea, se definesc metodele ce vor fi apelate la generarea evenimentelor datorate interactiunii utilizatorului cu controalele grafice. Mai jos sint descrise aceste componente si actiunile asociate:
butoane:
Toate butoanele au asociate handlere de tratare a evenimentelor generate de apasarea butonului. Constructia unui astfel de handler e prezentata mai jos:
<nume_buton>.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
<nume_buton>_actionPerformed(e);
}
}
);
La actionarea butonului se genereaza un eveniment e de tipul ActionEvent care conduce la executia metodei <nume_buton>_actionPerformed(e).
Functionalitatile butoanelor:
Register – actionarea lui conduce la inregistrarea clientului in grupul de discutii. Dupa actionare, devine inactiv.
Step Back – prin intermediul lui clientul anunta managerul ca paraseste grupul de discutii. Devine activ dupa inregistrarea clientului.
Speak – este utilizat de catre client pentru a solicita dreptul de a introduce informatie sub forma de text. Dupa primirea acestui drept devine inactiv, pina la cedarea sau prelevarea dreptului.
Over – pereche a lui Speak. Prin intermediul lui, clientul anunta managerul ca cedeaza dreptul de a difuza text. Se afla in starea opusa butonului Speak.
Draw – clientul actioneaza acest buton pentru a solicita managerului dreptul de a difuza informatie de tip grafic. Devine inactiv dupa primirea acestui drept si pina la cedarea sau prelevarea acestuia.
Over – dualul lui Draw. La actionarea sa, managerul este anuntat de renuntarea dreptului la difuzarea informatiei grafice. Se gaseste in starea opusa lui Draw.
Deliver URL – este actionat de catre client pentru a difuza celorlalti participanti la grupul de discutii o adresa URL la care se afla o clasa Java.
Accept Demo – odata primita adresa URL, prin actionarea acestui buton, clientul accepta aducerea clasei pe masina locala, incarcarea ei si executia codului.
suprafata destinata introducerii de informatie sub forma de text
Are asociat un handler de tratare a evenimentelor produse ca urmare a introducerii textului de la tastatura:
chatArea.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(KeyEvent e) {
chatArea_keyPressed(e);
}
});
Fiecare caracter introdus determina aparitia unui eveniment e de tip KeyEvent care declanseaza executia metodei chatArea_keyPressed(e).
suprafata destinata introducerii de informatie sub forma grafica
Pe aceasta suprafata se poate desena liber cu mouse-ul. Are asociate doua handlere de evenimente:
canvas.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
canvas_mouseDragged(e);
}
});
canvas.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(MouseEvent e) {
canvas_mousePressed(e);
}
});
Primul handler se ocupa de evenimentele produse ca urmare a tiririi mouse-ului pe suprafata de desenat, existind cel putin un buton apasat. Cel de-al doilea handler este asociat actiunii de apasare a unui buton al mouse-ului.
In cazul ambelor handlere, rutinele de tratare sint apelate cu un parametru de tipul MouseEvent.
Ca handler, este utilizat un obiectal clasei MouseAdapter, clasa la a carei instantiere este necesara definirea doar a metodelor ce prezinta interes.
In afara de controalele discutate anterior, mai exista suprafete destinate afisarii de informatie sub forma de text. Ele nu au handlere asociate. Informatia pe care o contin la un moment dat este preluata in urma actiunii altor controale.
Constructia obiectului ce furnizeaza date canalului de comunicatie.
Constructia obiectului ce preia date din canalul de comunicatie.
Declararea si definirea metodei launchDemo care are rolul de a incarca o clasa de pe Web si a o executa local.
Producer – constructorul acestei clase primeste ca parametru numele canalului de comunicatie la care trebuie sa se conecteze. Codul constructorului se prezinta astfel:
Producer(String chan) {
try {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
org.omg.CORBA.BOA boa = orb.BOA_init();
// get a channel
String chanHost = Address.getAddress("ChannelAddress");
EventChannel channel = EventChannelHelper.bind(orb, chan, chanHost, null);
System.out.println("Producer located event channel " + chan + ": " + channel);
// get a proxy
pushConsumer = channel.for_suppliers().obtain_push_consumer();
System.out.println("Obtained push consumer: " + pushConsumer);
// producer is ready to answer
boa.obj_is_ready(this);
System.out.println("Created producer: " + this);
// producer connects
System.out.println("producer connecting…");
pushConsumer.connect_push_supplier(this);
}
catch(Exception e) {
System.out.println("Producer " + this + " broke down");
e.printStackTrace();
}
}
Dupa obtinerea unei instante a clasei ORB si a unui adaptor BOA, urmeaza inregistrarea acestui obiect CORBA la ORB si conectarea la canalul de comunicatie, a carei adresa este obtinuta, dintr-un fisier local, prin intermediul metodei statice Address.getAddress. In urma obtinerii unei referinte a obiectului de tip EventChannel, urmeaza procedura, descrisa in sectiunea Comunicare, pentru obtinerea unui obiect de tip PushConsumer si conectarea la canalul de comunicatie. Obiectul Producer urmeaza a folosi metoda push a obiectului CORBA pushConsumer.
Aceasta clasa implementeaza interfata PushSupplier, deci este necesara implementarea metodei disconnect_push_suplier, discutata in sectiunea Comunicare:
public void disconnect_push_supplier() {
System.out.println("Producer " + this + " disconnected");
try {
_boa().deactivate_obj(this);
}
catch(org.omg.CORBA.SystemException e) {
e.printStackTrace();
}
}
La apelul acestei metode, obiectul Producer este deconectat de la ORB.
EventConsumer – obiectul este construit dindu-i-se ca parametru chiar referinta la obiectul de tip PartyLineFrame.Cele doua obiecte trebuie sa cunoasca, fiecare, referinta celuilalt pentru ca isi apeleaza, reciproc, o parte din metode. Acest obiect detine rolul de a prelucra informatia preluata din canalul de comunicatie. Iata codul ce construieste acest obiect:
public EventConsumer(PartyLineFrame p) {
try {
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
org.omg.CORBA.BOA boa = orb.BOA_init();
// canvas
this.p = p;
String chanHost = Address.getAddress("ChannelAddress");
EventChannel channel = EventChannelHelper.bind(orb, p.eventChan, chanHost, null);
System.out.println("Located event channel: " + channel);
boa.obj_is_ready(this);
System.out.println("Created draw consumer: " + this);
ProxyPushSupplier pushSupplier = channel.for_consumers().obtain_push_supplier();
System.out.println("Obtained push supplier: " + pushSupplier);
System.out.println("Connecting…");
pushSupplier.connect_push_consumer(this);
// start the event drawer
edt = new Thread(new EventDrawer(p));
edt.start();
}
catch(Exception e) {
e.printStackTrace();
}
}
Dupa localizarea canalului de comunicatie, realizata in acelasi mod ca si obiectul anterior, urmeaza obtinerea obiectului proxy PushSupplier si conectarea la canalul de comunicatie. In afara de aceste actiuni, se remarca si pornirea, in cadrul unui thread, a obiectului EventDrawer, despre care se va discuta mai jos.
Acest obiect implementeaza metoda disconnect_push_consumer, datorita faptului ca extinde interfata PushConsumer, despre care s-a discutat in sectiunea Comunicare.
In plus, fata de Producer, EventConsumer trebuie sa implementeze metoda push care este apelata de catre obiectul proxy cu care comunica acest obiect ori de cite ori este furnizat un nou obiect de tip Any de catre canalul de comunicatie.
EventDrawer – obiect construit de catre EventConsumer. Implementeaza interfata Runnable si metoda sa run are rolul de a prelua evenimentele de tip grafic dintr-un buffer local si a realiza transformarea acestei informatii intr-o imagine corespunzatoare celei desenate de catre clientul ce detine in mod curent acest drept.
Constructorul acestui obiect primeste drept unic parametru referinta la obiectul PartyLineFrame.
TextSender – obiect construit de metoda register_actionPerformed a obiectului PartyLineFrame. Se ocupa cu preluarea din buffer-ul local a sirurilor de caractere produse de client si de difuzarea acestora celorlalti clienti. Implementeaza interfata Runnable. Constructorul sau primeste ca parametru referinta la obiectul PartyLineFrame.
TextDrawer – obiect construit de metoda register_actionPerformed a obiectului PartyLineFrame. Se ocupa cu preluarea din buffer-ul local a obiectelor rezultate in urma prelucrarilor evenimentelor produse ca de miscarea mouse-ului pe suprafata de desenare si de difuzarea acestora celorlalti clienti. Implementeaza interfata Runnable. Constructorul sau primeste ca parametru referinta la obiectul PartyLineFrame.
MsgElem – instantele acestui obiect incapsuleaza coordonatele unui eveniment de tipul MouseEvent. Aceste elemente pot forma o lista simplu inlantuita. Codul este urmatorul:
public class MsgElem {
MsgElem next;
int x, y;
MsgElem() {
x = y = 0;
}
MsgElem(int x, int y) {
this.x = x;
this.y = y;
}
}
MsgList – reprezinta o clasa ale carei instante sint utilizate de componente ale pachetului Client pentru a comunica. Implementeaza un buffer si ofera metodele asociate pentru a introduce si a scoate elemente din buffer. Buffer-ul contine elemente de tipul MsgElem, prezentat, anterior, si formeaza o lista simplu inlantuita. Codul este prezentat mai jos:
public class MsgList {
MsgElem head;
MsgElem tail;
int count = 0;
// enqueues one's request
boolean add(MsgElem e) {
boolean first = false;
if (head == null) {
head = tail = e;
first = true;
}
else
tail = tail.next = e;
count++;
return first;
}
// add a list of elements
void addBlock(DrawMsg tab[]) {
int i;
for (i = 0; i < tab.length; i++)
add(new MsgElem(tab[i].x, tab[i].y));
}
// dequeues oldest element
MsgElem retrieve() {
MsgElem e = null;
if (head != null){
e = head;
head = head.next;
count–;
}
return e;
}
}
MyClassLoader – acest obiect este utilizat la aducerea codului unei clase de pe Web, incarcarea si executia ei pe masina locala. Clasa extinde ClassLoader, printre altele, metodele defineClass si resolveClass. Constructorul clasei primeste ca argument adresa URL a clasei ce trebuie adusa. Metoda loadClassFromInternet construieste un obiect de tip URL si deschide un flux; apoi, utilizind un buffer de 512 octeti, citeste de pe flux codul existent la adresa respectiva, construind un vector de octeti pe masina locala:
public byte[] loadClassFromInternet() {
final int bytes = 512;
byte b[] = null;
byte buff[] = new byte[bytes];
URL url = null;
Object o = null;
try {
url = new URL(addr);
BufferedInputStream bf = new BufferedInputStream(url.openStream());
int l = 0, crt = 0;
b = new byte[bf.available()];
while ((l = bf.read(buff, 0, bytes)) != -1) {
System.arraycopy(buff, 0, b, crt, l);
crt += l;
}
bf.close();
}
catch(Exception e) { e.printStackTrace(); }
return b;
}
Dupa ce codul a fost transferat pe masina locala, el este prelucrat de metoda loadClass. Aceasta metoda primeste ca parametru numele clasei si intoarce un obiect instanta a clasei Class. Acest obiect este construit de catre JVM pentru orice instantiere a unei clase. In cadrul metodei, se incearca, pe rind:
incarcarea clasei din tabela Hash locala, a claselor deja incarcate;
incarcarea clasei ca si clasa sistem;
incarcarea clasei din Internet, definirea si rezolvarea ei.
Metodele si constructorii unui obiect creat prin intermediul unui incarcator de clase pot referi alte clase. Pentru a determina aceste clase, JVM apeleaza metoda loadClass a incarcatorului de clase utilizat pentru a crea noua clasa. Din cauza faptului ca se doreste crearea unei instante din noua clasa, aceasta trebuie rezolvata, dupa ce este definita.
Definirea clasei se realizeaza prin intermediul metodei recursive defineClass. Aceasta metoda converteste un sir de octeti intr-o instanta a clasei Class. Ea este apelata pentru orice clasa a carei instanta este utilizata in clasa nou construita. Dupa aceea, se va apela metoda resolveClass. Aceasta metoda este apelata de catre metoda loadClass doar daca parametrul boolean actual al acesteia are valoarea true.
Rezultatul acestor prelucrari va fi un obiect al clasei Class. Pentru a se crea o instanta a acestei clase, se va apela metoda Class.newInstance.
Mai jos este prezentat codul metodei loadClass:
public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = null;
byte cRow[] = null;
// first lookup in hash
c = (Class) hash.get(name);
if ( c != null ) {
return c;
}
// try to load it as system class
try {
c = findSystemClass(name);
}
catch (ClassNotFoundException e) {
// it isn't a system class
cRow = loadClassFromInternet();
c = defineClass(name, cRow, 0, cRow.length);
}
resolveClass(c);
hash.put(name, c);
return c;
}
4.4.2 Manager
Grupul Manager are in componenta clase ce se ocupa cu conducerea si coordonarea aplicatiei, ca si cu transferul de informatii.
Din grupul Manager fac parte urmatoarele clase:
ManagerFrame – reprezinta clasa ce contine punctul de intrare in programul de pe masina managerului. Contine metoda main si extinde clasa DecoratedFrame, fiind, astfel, clasa care se ocupa si de interfata grafica a grupului Manager.
Metoda main este asemanatoare cu cea a clasei PartyLine dar contine, in plus, sectiunea de constructie si inregistrare la ORB a obiectului furnizor de servicii, TalkManagerImpl. Aceasta secventa de cod este prezentata in continuare:
// create and initialize the ORB
ORB orb = ORB.init(args, null);
// create servant and register it with the ORB
frame.tm = new TalkManagerImpl("Manager", frame);
orb.connect(frame.tm);
// waiting for calls
java.lang.Object o = new java.lang.Object();
synchronized(o) { o.wait(); }
Variabila frame contine referinta obiectului ManagerFrame, iar tm este referinta la serverul de metode CORBA, TalkManager Impl.
Initializarea interfetei grafice se realizeaza tot in cadrul metodei jbInit. Controalele prin care utilizatorul poate interactiona cu interfata se rezuma la doua butoane, avind urmatoarele functionalitati:
Stop Speaker – actionarea lui conduce la prelevarea dreptului de a difuza informatie sub forma de text a clientului ce detine in mod curent acest drept;
Stop Drawer – aceasi functiune ca si Stop Speaker, numai ca se refera la clientul care poate introduce informatie de tip grafic.
Ambele butoane au inregistrate handlere de evenimente, avind aceeasi forma ca cele discutate in cadrul interfetei client. La actionarea unui buton se produce un eveniment de tipul ActionEvent care determina apelul metodei asociate handlerului.
TalkManagerImpl – reprezinta chiar obiectul ale carui metode sint apelate de catre clienti, acesta fiind inregistrat ca obiect CORBA. Implementarea aceastei clase pleaca de la o interfata Java rezultata in urma utilizarii compilatorului idl2java asupra interfetei IDL CORBA de mai jos:
interface TalkManager {
string registerClient();
void request(in CmdMsg msg);
};
In urma compilarii au rezultat fisierele stub si skeleton specifice. Clasa TalkManagerImpl extinde clasa abstracta
abstract public class _TalkManagerImplBase extends org.omg.CORBA.portable.Skeleton implements PartyLine.TalkManager
Constructorul clasei TalkManagerImpl primeste drept argument referinta la obiectul ManagerFrame.
In cadrul constructorului se obtine o instanta a clasei Producer, discutata la sectiunea CLIENTI. Acest obiect serveste la transmiterea informatiei spre canalul de comunicatie.
Clasa TalkManagerImpl contine lista clientilor conectati la grupul de discutii. Acesti clienti sint inregistrati intr-o tabela Hash, instanta a clasei Hashtable.
De asemenea, exista cite un obiect de tip List pentru coada de clienti care asteapta dreptul de a difuza text, respectiv coada clientilor aflati in asteptarea dreptului de a difuza informatie grafica.
List – implementeaza o lista simplu inlantuita de elemente ce reprezinta instante ale clasei Elem. Codul se prezinta in felul urmator:
public class Elem {
Elem next;
String name;
Elem(String name) {
this.name = name;
}
}
Clasa List prezinta urmatoarele metode:
boolean add(String name) – adauga un element listei;
String retrieve() – extrage din coada cel mai vechi element;
public boolean remove(String name) – elimina din coada un element.
Ca si clasa MsgList, si in acest caz se utilizeaza politica FIFO.
4.4.3 Canalul de comunicatie
Acest obiect este utilizat la transmiterea informatiei intre diferitele entitati participante la grupului de discutii. Dupa cum am precizat si in sectiunea COMUNICARE, mecanismul folosit pentru comunicare respecta paradigma producator – consumator. Anterior, au fost descrise obiectele ce au rolul de a produce, respectiv de a consuma informatie.
Mediul de transmisie este implementat, practic, ca o colectie de obiecte CORBA, inregistrate la implementarea ORB de pe masina unde este instantiat canalul de comunicatie. Din aceasta colectie fac parte obiectele proxy ce se afla la extremitatile logice ale canalului de comunicatie si au rolul de a comunica cu producatorii, respectiv consumatorii.
Modelul de comunicare este push, dupa cum s-a mentionat anterior.
Canalul de comunicatie este o instanta a clasei
com.visigenic.vbroker.services.CosEvent.Channel.
El este lansat in executie ca si server, obiectele CORBA de tip proxy asteptind solicitarile producatorilor si consumatorilor. Odata informatia transmisa obiectelor proxy consumator, ea este transmisa, intern, obiectelor proxy producator, fiecare din acesta avind asociata o coada de evenimente. Fiecare obiect proxy dialogheaza cu un producator sau un consumator.
4.4.4 Fluxul programului
Conform cu cele discutate in cadrul capitolului referitor la interfata grafica, unde s-au prezentat, pe rind, cele patru sectiuni, se va pastra acelasi mod de lucru, abordind, secvential, aceleasi sectiuni.
Sectiunea conectare – deconectare a clientului la grupul de discutii
La pornirea programului, singurele butoane active ale interfetei sint Register si Step Back. Desi conexiunea cu canalul de comunicatie este realizata, clientul nu poate transmite informatie de nici un tip, nefiind, inca, inregistrat. Legatura cu managerul nu este, inca, realizata.
Prin actionarea butonului Register, clientul obtine o referinta la obiectul CORBA implementat de manager. Adresa masinii pe unde ruleaza managerul se obtine dintr-un fisier de pe discul local, prin intermediul metodei statice Address.getAddress. De asemenea, butoanele prin intermediul carora clientul poate solicita servicii devin active si sint pornite thread-urile care se ocupa de colectarea informatiei din buffer-ele locale si trimiterea ei spre canalul de comunicatie. Codul asociat etapei de inregistrare este prezentat in continuare:
void register_actionPerformed(ActionEvent e) {
String args[] = null;
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
// Locate an account manager.
String manHost = Address.getAddress("ManagerAddress");
tm = TalkManagerHelper.bind(orb,"Manager", manHost, null);
// client shouldn't register twice
register.setEnabled(false);
// client gets an ID as result of registration
name = tm.registerClient();
// client may request to speak
speak.setEnabled(true);
// client may request to draw
draw.setEnabled(true);
// client may request to broadcast a demo class
deliverURL.setEnabled(true);
// build up a thread which grabbs mouse events from local buffer
// and sends them into the Event Channel
drawSender = new Thread(new DrawSender(this));
// build up a thread which grabbs text from local buffer
// and sends it into the Event Channel
textSender = new Thread(new TextSender(this));
}
Variabila tm contine o referinta la obiectul CORBA ce reprezinta managerul. In urma apelului metodei tm.registerClient, clientul obtine un identificator unic pe care il va folosi in cererile ulterioare. Clientul este introdus in baza de date a managerului, implementata printr-o tabela Hash, ale carei chei sint chiar identificatorii clientilor.
Retragerea din cadrul grupului de discutii se realizeaza prin actionarea butonului Step Back. Acest lucru atrage dupa sine executia metodei urmatoare:
void stepBack_actionPerformed(ActionEvent e) {
if (!drawOn && !speakOn) {
// inform manager he will quit
try { tm.request(new CmdMsg(name, 'r')); }
catch(Exception e1) { e1.printStackTrace(); }
System.exit(0);
}
}
Retragerea se poate realiza doar in cazul in care clientul nu detine dreptul de a transmite text sau informatie grafica. Clientul trebuie sa cedeze toate drepturile inainte de a putea parasi grupul de discutii. Aceasta solutie a fost aleasa pentru a evita traficul suplimentar de mesaje, tinind seama de restrictiile tehnice discutate in sectiunea teoretica. Apoi, construieste un obiect al clasei CmdMsg, incapsulind in acesta cererea sa si apelind metoda tm.request. Clasa CmdMsg reprezinta reflectarea in Java a unei structuri IDL. Iata cum se prezinta aceasta structura:
// command message
struct CmdMsg {
string name;
char cmd;
};
Metoda tm.request este utilizata pentru toate cererile sau informatiilece sint adresate managerului. In functie de cererea sau informatia transmisa, se va executa una din metodele locale de pe masina managerului, care va rezolva problema. In cazul de fata, se va executa metoda abortClient, al carei cod este prezentat mai jos:
// allow client to step back
private void abortClient(String name) {
if (sList.remove(name))
// one speaker dequeued
mf.speakersNo.setText((new Integer(–speakers)).toString());
if (dList.remove(name))
// one drawer dequeued
mf.drawersNo.setText((new Integer(–drawers)).toString());
// remove client
pool.remove(name);
mf.clientsOn.setText((new Integer(–clients)).toString());
}
In cadrul acestei metode se cerceteaza daca clientul se afla in asteptare in vreuna din cele doua cozi de asteptare si, daca da, este eliminat din acea coada. Eliminarea din tabela Hash se face prin intermediul apelului metodei pool.remove. De asemenea, sint actualizate informatiile afisate in interfata manager.
Sectiunea chat – vehicularea informatiei sub forma de text
Informatia sub forma de text este furnizata de catre client prin editarea suprafetei din containerul cu nume Chat Section. Pentru a putea transmite text, clientul trebuie sa solicite acest drept managerului, actionind butonul Speak. Acest lucru duce la executia metodei prezentate in continuare:
void speak_actionPerformed(ActionEvent e) {
speak.setEnabled(false);
over.setEnabled(true);
// request the manager permision to speak
try { tm.request(new CmdMsg(name, 's')); }
catch(Exception e1) { e1.printStackTrace(); }
}
La executia metodei, butoanele speak si over isi interschimba starile, ceea ce inseamna ca nu se mai poate solicita acest drept pina cind el nu este primit si cedat sau prelevat.
Apelul metodei tm.request conduce la executia urmatoarei metode pe masina manager:
// manage speech requests
public synchronized void speechRequest(String name) {
if (speaker == null) {
speaker = name;
mf.speaker.setText(speaker);
mf.speakerDate.setText(getDate());
// send the answer to the client
try {
CmdMsgHelper.insert(any, new CmdMsg(name, 'S'));
cmdProd.pushConsumer.push(any);
}
catch(Exception e) { e.printStackTrace(); }
}
else {
sList.add(name);
mf.speakersNo.setText((new
Integer(++speakers)).toString());
}
}
In cadrul metodei de mai sus, managerul verifica daca exista un alt client care detine dreptul de a introduce text.
Daca nu, clientul solicitant primeste acest drept si i se trimite confirmarea prin apelul metodei cmdProd.pushConsumer.push, metoda apartinind obiectului proxy cu care dialogheaza obiectul Producer asociat managerului.
Daca da, atunci cererea clientului actual este introdusa intr-o coada de asteptare.
In momentul in care clientul primeste dreptul de a difuza text, suprafata destinata editarii se activeaza. Comanda trimisa de manager prin intermediul canalului de comunicatie este preluata de catre obiectul EventConsumer asociat clientului. Acesta decide ce actiune va avea loc in urma verificarii tipului mesajului incapsulat in tipul CORBA generic Any, destinatiei si continutului mesajului. In cazul de fata se va executa metoda EventConsumer.executeComand care interpreteaza comanda si decide actiunile urmatoare. Secventa de cod este urmatoarea:
// enable speaking
p.speakOn = true;
// start the thread wich sends text events into the //EventChannel
if (p.textSender.isAlive()) {
p.textSender.resume();
}
else
p.textSender.start();
// allow client to write
p.chatArea.setEnabled(true);
p.statusSpeak.setText("can write");
break;
Dupa ce clientului ii este permis, in mod practic, sa transmita text, prin setarea flag-ului p.speakOn apartinind metodei PartyLineFrame, si prin trecerea suprafetei de editare in starea enabled, se verifica daca thread-ul care se ocupa de colectarea textului din buffer-ul local si transmiterea lui canalului de comunicatie este suspendat. In caz pozitiv, acesta este repus in stare de executie. Altfel, este creat si adus in starea gata de executie.
Fiecare caracter introdus de catre client genereaza un eveniment de tip KeyEvent, eveniment tratat de catre rutina asociata handlerului de evenimente adecvat.Aceasta rutina verifica tipul caracterului si, daca nu face parte din categoria CHAR_UNDEFINED, il introduce in buffer-ul local. Accesul la acest buffer este sincronizat pe monitorul asociat obiectului PartyLineFrame. Aceasta solutie a fost aleasa deoarece, pe de-o parte, buffer-ul local este o variabila ce contine referinta catre un obiect de tip StringBuffer, obiect inlocuit cu unul nou creat la preluarea textului din buffer si, pe de alta parte, obiectul clasei PartyLineFrame nu mai este utilizat in nici un alt context pentru sincronizare.
Din buffer-ul local textul este preluat de catre thread-ul in cadrul caruia evolueaza un obiect din clasa TextSender. Metoda run a acestei clase preia textul din buffer, il impacheteaza intr-un obiect din clasa org.omg.CORBA.Any si-l transfera canalului de comunicatie. Secventa de cod este prezentata in continuare:
// allow event listener to access text more often
Thread.currentThread().setPriority(1);
while (true) {
synchronized(p) {
s = p.text.toString();
p.text = new StringBuffer();
}
try {
if (!s.equals("")) {
any.insert_string(s);
p.eventProd.pushConsumer.push(any);
}
}
catch(Exception e) { e.printStackTrace(); }
}
Tipul string din IDL CORBA se mapeaza pe tipul String din Java, deci nu este necesara utilizarea unei structuri speciale pentru a impacheta informatia intr-un obiect CORBA.
Prioritatea acestui thread este scazuta la minim pentru a permite threadului care scrie in buffer sa introduca mai multe caractere, inainte ca acestea sa fie impachetate si trimise canalului de comunicatie.
Dupa ce au fost transmise canalului de comunicatie prin apelul metodei push a obiectului Producer asociat, obiectele vor fi preluate de catre consumatori, prin intermediul obiectului EventConsumer. Dupa ce este extrasa informatia din obiectul Any primit, se verifica daca acest client nu este totuna cu cel care a trimis textul si, in caz afirmativ, textul este afisat in cadrul ferestrei din sectiunea ChatSection a interfetei grafice.
Transmiterea de text are loc pina cind clientul actioneaza butonul Over sau pina cind este oprit de catre manager.
La actionarea butonului Over, se verifica daca clientul era activ. In caz afirmativ, se comunica managerului decizia, prin apelul metodei tm.request si se modifica starea controalelor grafice in consecinta. Iata codul:
void over_actionPerformed(ActionEvent e) {
// stop client from writing
if (speakOn) {
chatArea.setEnabled(false);
// announce Manager he cease speaking
try { tm.request(new CmdMsg(name, 'c')); }
catch(Exception e1) { e1.printStackTrace(); }
// comunnicate all speaking related processes to stop
speakOn = false;
over.setEnabled(false);
speak.setEnabled(true);
statusSpeak.setText("can't write");
}
}
In cazul in care managerul decide sa-l opreasca pe client, este actionat butonul Stop Speaker. Acest lucru declanseaza executia metodei locale tm.forceStepBack a obiectului TalkManagerImpl. In cadrul acestei metode este apelata metoda stepBack, discutata anterior, si este trimisa clientului comanda de intrerupere a difuzarii de text. Clientul preia mesajul prin intermediul obiectului EventConsumer si executa urmatorul cod:
// disable keyboard events management
p.speakOn = false;
p.textSender.suspend();
// stop client from writing
p.chatArea.setEnabled(false);
p.speak.setEnabled(true);
p.over.setEnabled(false);
p.statusSpeak.setText("can't write");
In afara de schimbarile suferite de catre interfata grafica si flag-ul p.speakOn, se remarca suspendarea executiei thread-ului care se ocupa de preluarea textului din buffer-ul local si trimiterea lui canalului de comunicatie.
In acest moment, situatia este cea dinantea cererii clientului de a obtine dreptul de difuzare a textului.
Sectiunea draw – transferul informatiei de tip grafic
Informatia transmisa reprezinta rezultatul actiunii de desenare libera, cu ajutorul mouse-ului, pe suprafata special destinata.
Pentru a obtine dreptul de a desena, clientul trebuie sa actioneze butonul Draw. In urma acestei actiuni este apelata, de catre handlerul de evenimente, metoda urmatoare:
void draw_actionPerformed(ActionEvent e) {
draw.setEnabled(false);
overd.setEnabled(true);
// request the manager permision to draw
try { tm.request(new CmdMsg(name, 'd')); }
catch(Exception e1) { e1.printStackTrace(); }
}
In urma executiei acestei metode, este transmisa managerului cererea clientului, prin apelul metodei tm.request, tm fiind referinta la obiectul CORBA TalkManagerImpl. De asemenea, se inverseaza starile butoanelor Draw si Over.
Managerul examineaza cererea venita de la client, luind o decizie. Aceasta decizie este luata in urma executiei metodei urmatoare, locala managerului:
public synchronized void drawRequest(String name) {
if (drawer == null) {
drawer = name;
mf.drawer.setText(drawer);
mf.drawerDate.setText(getDate());
// send the answer to the client
try {
CmdMsgHelper.insert(any, new CmdMsg(name, 'D'));
cmdProd.pushConsumer.push(any);
}
catch(Exception e) { e.printStackTrace(); }
}
else {
dList.add(name);
mf.drawersNo.setText((new Integer(++drawers)).toString());
}
}
Se verifica daca exista un client care deseneaza, in mod curent. In caz negativ, este cedat dreptul de a desena solicitantului, prin formularea unui mesaj de raspuns si trimiterea acestuia prin canalul de comunicatie. In caz pozitiv, cererea solicitantului este introdusa intr-o coada de asteptare.
In momentul in care clientul primeste dreptul de a desena, obiectul EventConsumer asociat lanseaza in executie metoda executeCommand, din care se va executa urmatoarea sectiune de cod:
// enable mouse events to be managed
p.drawOn = true;
// start the thread wich sends mouse events into the Event Channel
if (p.drawSender.isAlive()) {
p.drawSender.resume();
}
else
p.drawSender.start();
p.statusDraw.setText("can draw");
In urma executiei acestui cod, se seteaza flag-ul p.drawOn la valoarea true, se aduce in starea gata de executie thread-ul ce executa metoda run a obiectului DrawSender si se modifica in mod corespunzator starea interfetei clientului.
Clientul deseneaza prin apasarea unui buton al mouse-ului si tirirea cursorului in interiorul conturului suprafetei de desenare, din sectiunea Draw Section. Atit la apasarea butonului, cit si la fiecare oprire din tirire a cursorului, se genereaza evenimente de tipul MouseEvents. Fiecare dintre cele doua actiuni mentionate are asociata cite-un handler de evenimente. La apasarea unui buton al mouse-ului, evenimentul generat este prelucrat de urmatoarea metoda:
// put in the local buffer the starting point of a polyline
void canvas_mousePressed(MouseEvent e) {
if (drawOn) {
synchronized(l) {
l.add(new MsgElem(-1000, 0));
l.add(new MsgElem(e.getX(), e.getY()));
}
}
}
Se adauga in buffer-ul local atit un element cu rol de marcare, cit si punctul in care s-a produs evenimentul. Coordonatele punctului sint utilizate la constructia unui obiect de tipul MsgElem care este introdus in lista ce reprezinta buffer-ul. Elementul cu rol de marcare este introdus pentru a delimita inceputul unei noi actiuni de trasare pe suprafata de desenare. Operatia de adaugare a elementelor in lista este sincronizata chiar pe obiectul ce reprezinta lista.
La generarea fiecarui eveniment datorat tiririi mouse-ului pe suprafata de desenare este declansata de catre handlerul de evenimente asociat executia urmatoarei metode:
void canvas_mouseDragged(MouseEvent e) {
if (drawOn) {
synchronized(l) {
l.add(new MsgElem(e.getX(), e.getY()));
}
}
}
Si in acest caz, sincronizarea se realizeaza asupra buffer-ului local.
Ambele metode prezentate anterior se executa numai daca clientul are dreptul de a desena.
In paralel cu aceste metode evolueaza un fir de executie cu urmatoarea metoda run:
// allow event listener to access l more often
Thread.currentThread().setPriority(1);
while (true)
if (p.l.head != null) {
// grabs mouse events from local buffer
synchronized(p.l) {
msgTab = new DrawMsg[count=p.l.count];
for (int i = 0; i < count; i++) {
MsgElem me = p.l.retrieve();
msgTab[i] = new DrawMsg(me.x, me.y);
}
p.l.head = p.l.tail = null;
} // synchronized
// draw locally
p.eventCons.drawMsgs(msgTab);
// encode events from local buffer and send them through EventChannel
try {
DrawMsgSeqHelper.insert(any, msgTab);
p.eventProd.pushConsumer.push(any);
}
catch(Exception e) { e.printStackTrace(); }
} // if
// while
Acest cod apartine obiectului DrawSender si are o functionalitate multipla:
extrage in mod sincronizat elementele din buffer-ul local;
construieste, pentru fiecare element din lista, de tipul MsgElem, un obiect provenit din implementarea Java a unei structuri IDL CORBA:
// IDL
struct DrawMsg {
long x, y;
};
formeaza un vector cu aceste elemente;
deseneaza local, prin apelul metodei p.eventCons.drawMsgs;
converteste tabloul obtinut intr-un obiect Any si-l transmite canalului de comunicatie, pentru a fi trimis celorlalti clienti.
Tabloul de obiecte construit este reflectarea in Java a urmatoarei constructii IDL:
// sequence of messages
typedef sequence<DrawMsg> DrawMsgSeq;
Metoda drawMsgs a obiectului de tip EventConsumer are urmatoarea implementare:
public void drawMsgs(DrawMsg m[]) {
for (int i = 0; i < m.length; i++)
if (m[i].x == -1000) {
x = m[++i].x;
y = m[i].y;
}
else {
p.canvas.drawOnCanvas(x, y, m[i].x, m[i].y);
x = m[i].x;
y = m[i].y;
} // if
// for
}
In corpul metodei se parcurg toate elementele vectorului transmis ca parametru si, in cazul ca este vorba de un element de marcare, coordonatele punctui din obiectul urmator sint memorate. Daca este un obiect obisnuit, atunci se traseaza o linie de la ultimul punct memorat si pina in punctul actual, iar apoi coordonatele memorate devin coordonatele punctului actual. Aceasta solutie a fost aleasa pentru a evita, pe cit posibil, trasarea unei linii formate din puncte intre care distanta este sesizabila.
In cadrul metodei de mai sus este apelata metoda p.canvas.drawOnCanvas. Aceasta metoda este oferita de catre obiectul clasei CanvasBean, obiect de tip JavaBean construit special pentru scopurile acestui program. Aceasta clasa extinde clasa java.awt.Canvas si redefineste metodele update si paint conform cu cerintele programului.
La receptia unui mesaj de tipul DrawMsg[], obiectul EventDrawer va decide executia urmatoarei sectiuni de cod:
// draw
if (!p.drawOn) {
DrawMsg m[] = DrawMsgSeqHelper.extract(any);
synchronized(l) {
l.addBlock(m);
}
}
Se observa ca, spre deosebire de cazul in care este receptionata informatie sub forma de text, in acest caz nu se realizeaza reflectarea informatiei primite, pe suprafata de desen, in mod direct. Informatia este inclusa intr-un buffer local obiectului EventConsumer, implementat printr-o lista simplu inlantuita. Aceasta solutie a fost adoptata datorita faptului ca metoda push, cea care determina executia codului de mai sus, nu este o metoda sincronizata. EventConsumer este un obiect CORBA ce ofera metoda push, printre altele. Obiectul proxy producator al canalului de evenimente, cel care apeleaza aceasta metoda, poate determina (in practica, chiar determina) executia concurenta a codului metodei drawOnCanvas, in cazul in care aceasta ar fi apelata de aici. Apelul concurent al acestei metode implica executia, tot in mod concurent, ale unor operatii asupra suprafetei de desenare. In urma acestei executii concurente, rezultatul afisat de catre suprafata de desenare devine incorect, multa informatie fiind pierduta.
Pentru a remedia acest inconvenient, are loc o decuplare a actiunilor de receptie a informatiei cu caracter grafic, si, respectiv, de prelucrare si reflectare a acesteia pe suprafata de desenare. Decuplarea se realizeaza prin intermediul buffer-ului deja amintit. Preluarea informatiei din acest buffer este realizata de catre un obiect de tip EventDrawer, a carei metoda run este executata in cadrul unui fir de executie separat. Codul acestei metode este prezentat mai jos:
Thread.currentThread().setPriority(1);
while (true)
if (p.eventCons.l.head != null) {
// grabs mouse events from local buffer
synchronized(p.eventCons.l) {
msgTab = new DrawMsg[count=p.eventCons.l.count];
for (int i = 0; i < count; i++) {
me = p.eventCons.l.retrieve();
msgTab[i] = new DrawMsg(me.x, me.y);
}
p.eventCons.l.head = p.eventCons.l.tail = null;
p.eventCons.l.count = 0;
} // synchronized
// draw locally
p.eventCons.drawMsgs(msgTab);
} // if
// while
Si in cazul acestui thread s-a preferat scaderea prioritatii pentru a favoriza firul de executie ce introduce secvente de elemente in buffer.
Solutiile de decuplare utilizate in aceasta sectiune decaleaza afisarea desenelor, dar, pe de alta parte, maresc frecventa cu care JVM preia evenimentele produse la nivelul sistemului de operare. Prioritatea firelor de executie care extrag elemente din buffer poate fi crescuta in functie de incarcarea prevazuta pentru JVM care va rula programul, tinind seama ca frecventa preluarii evenimentelor produse la nivelul SO este invers proportionala cu incarcarea masinii.
Incheierea difuzarii de informatie sub forma grafica poate fi decisa de catre client, prin actionarea butonului Over, sau de catre manager, prin actionarea butonului Stop Drawer. Actiunile ce au loc in aceste situatii sint similare celor din sectiunea shat.
Sectiunea destinata vehicularii continutului executabil
In cadrul acestei sectiuni, un participant la grupul de discutii poate pune la dispozitia celorlalti participanti o adresa URL la care se afla o clasa Java. Aceasta clasa implementeaza interfata Runnable si poate fi adusa pe masina locala, incarcata si executata. Alegerea interfetei Runnable este necesara pentru a evita scrierea pe discul local a clasei aduse de pe Web. Acest lucru ar fi fost necesar pentru a realiza cast de la Object la clasa incarcata de pe Web.
Un client care doreste sa puna la dispozitia celorlalti o adresa URL a unei clase trebuie sa actioneze butonul DeliverURL, nu inainte, insa, de a completa controlul grafic alaturat butonului cu adresa respectiva.
Odata actionat butonul, este declansata, de catre handlerul atasat butonului, actiunea descrisa de catre codul de mai jos:
URLMsgHelper.insert(any, new URLMsg(name, addr));
eventProd.pushConsumer.push(any);
Se construieste un obiect de tipul URLMsg ce corespunde structurii IDL urmatoare:
struct URLMsg {
string name;
string url;
};
Obiectul construit este impachetat si trimis tuturor celorlalti clienti.
Odata preluat de catre EventConsumer, obiectul ce incapsuleaza adresa este trecut metodei prezentata in continuare:
private void resolveURL(URLMsg msg) {
if (!msg.name.equals(p.name)) {
// announce client a new Demo URL has become available
p.urlDemo.setText(msg.url);
p.url = msg.url;
p.acceptURL.setEnabled(true);
}
}
In cimpul alaturat butonului Accept Demo al fiecarui client, cu exceptia sursei, va fi afisata adresa URL a clasei. In caz ca se doreste incarcarea clasei pe masina locala si executia ei, se va apasa butonul Accept Demo. Acest lucru are drept rezultat executia urmatorului cod:
// execute a class brought over Internet
private void launchDemo(String addr) {
byte b[] = null;
Class c = null;
String name = null;
StringTokenizer st = new StringTokenizer(addr, "\\/");
// get class name
while (st.hasMoreTokens())
name = st.nextToken();
// remove suffix ".class"
name = name.substring(0, name.length()-6);
try {
c = (new MyClassLoader(addr)).loadClass(name);
(new Thread((Runnable)c.newInstance())).start();
}
catch(Exception e) { e.printStackTrace(); }
}
}
Dupa ce este extras numele clasei din adresa primita se utilizeaza incarcatorul de clase MyClassLoader, descris intr-o sectiune anterioara, pentru a incarca clasa pe masina locala, definitie si rezolvare. Obiectul de tip Class obtinut este utilizat la crearea unei instante a clasei. Aceasta instanta implementeaza interfata Runnable, deci si metoda run care va fi executata in cadrul unui fir de executie.
Nu este unica solutie. S-ar fi putut utiliza si solutia unui Applet.
Pentru transferul de informatie de pe Web s-a preferat protocolul HTTP. Exista si solutia protocolului FTP, dar posibilitatea utilizarii lui este mult mai restrictiva.
Testare
Programul a fost testat atit pe statia de lucru pe care a fost elaborat, cit si intr-o retea locala compusa din calculatoare dotate cu procesoare avind frecventa cuprinsa intre 266-400 MHz si memorie interna 64 RAM. Sistemele de operare de pe aceste calculatoare au fost Windows 98 si Windows NT WorkStation.
Concluzii si directii de dezvoltare
5.1 Concluzii
Implementarea obtinuta acopera, din punct de vedere functional, dezideratele
exprimate in faza initiala a proiectului. Cu toate acestea, faptul ca programul permite transmiterea de informatie sub cele trei forme (text, grafica si continut executabil) nu inseamna ca acest lucru este realizat la nivelul maxim de performanta. Mai degraba, s-ar putea spune ca sint schitate trei idei de implementare, fiecare, in parte, putind fi subiectul unei abordari specifice care sa conduca la un rezultat optimizat pentru un anume context de utilizare.
Paradigma producator – consumator pare, din punct de vedere conceptual, potrivita pentru abordarea solutiei de implementare. Nu este singura posibilitate, comunicarea prin variabile partajate fiind o alta optiune de luat in seama. Mult mai important, insa, este mecanismul ce se regaseste in spatele unui anumit tip de implementare. Odata ajuns in acest punct, la alegerea mecanismului, a trebuit facuta constatarea discrepantei care exista intre serviciile oferite de standardul CORBA si implementarile diferitelor firme. Solutia de implementare a fost aleasa deoarece corespunde, din punct de vedere al modelului oferit, cu arhitectura sistemului. Dupa cum s-a mentionat si in sectiunile anterioare, acest mecanism nu este, inca, foarte bine pus la punct. Acest lucru s-ar putea explica, poate, prin faptul ca este destul de nou pentru a fi fost, deja, testat in diverse conjuncturi de functionare. Poate a fost mai important ca el sa fie prezentat ca o solutie pentru rezolvarea unui anumit tip de probleme, mai inainte de a-i fi definitivata implementarea si a fi facute optimizarile necesare.
De buna seama, in cazul in care situatia concreta ar fi permis, ar fi putut fi testate si comparate mai multe solutii de implementare, ceea ce ar fi condus, fara doar si poate, la o functionare mai buna a anumitor sectiuni de program.
Un alt aspect ce merita a fi mentionat se refera la problema evenimentelor produse ca urmare a actiunii mouse-ului asupra controalelor interfetei grafice. Dupa cum s-a mentionat, numarul evenimentelor produse la nivelul sistemului de operare este superior celor preluate de catre rutinele JVM. Acest lucru se datoreaza implementarii acestor rutine, SO utilizat si incarcarii JVM. Pe de alta parte, metoda nativa utilizata pentru desenare se comporta in mod diferit, in SO diferite. Exemplificarea se poate face rulind programul client, in aceleasi conditii, pe platformele Windows 98 si Windows NT Workstation. S-a constatat ca performantele sint mult mai bune pentru acesta din urma.
Ideile exprimate mai sus conduc la concluzia ca tipul platformei software este un factor important pentru buna functionare a programului, cel putin in acest stadiu.
Incercind o comparatie intre implementarea ce s-a dorit a fi realizata si ceea ce s-a obtinut, in fapt, poate fi remarcat faptul ca exista diferente semnificative datorate contextului hardware in care s-a realizat programul. Fiind vorba de un sistem distribuit de obiecte CORBA (Java), numarul de statii de lucru utilizate pentru teste, ca si performantele acestora, au o importanta majora pentru posibilitatea obtinerii unor concluzii corecte si alegerea unor solutii optime de implementare. Anumite situatii critice aparute nu au putut fi surmontate datorita faptului ca s-a utilizat, in marea parte a elaborarii programului, o singura statie de lucru, de performante medii. Astfel, se poate concluziona ca, o eventuala dezvoltare, ulterioara, a programului, realizata in conditii superioare dpdv hardware, ar putea conduce la performante mult sporite si la posibilitatea adaugarii de noi facilitati.
Citeva lucruri merita a fi mentionate despre tandemul Java – CORBA. Aceste doua entitati se complementeaza reciproc, in sensul ca Java furnizeaza cod mobil si portabil, executabil pe masina client, in timp ce CORBA furnizeaza infrastructura distribuita.
Java si CORBA par a fi realizate pentru a lucra impreuna. In recenta lucrare “Client / Server Programming with Java and CORBA”, Robert Orfali si Dan Harkey fac o afirmatie care, in traducere libera, ar suna astfel: “ Java este primul pas catre crearea unui obiect Web, dar nu este de ajuns. Java ofera o flexibilitate extraordinara pentru dezvoltarea aplicatiilor distribuite, dar, in prezent, nu suporta paradigma client-server. Pentru a face asta, Java are nevoie de o infrastructura de obiecte distribuite, loc unde apare ca solutie CORBA. CORBA furnizeaza veriga lipsa intre mediul aplicatiilor Java portabile si lumea serviciilor back-end. Intersectia dintre Tehnologiile obiect Java si CORBA este urmatorul pas natural in evolutia spre Object Web ”.
Modelul obiectual Java – CORBA este convergent in ceea ce priveste urmatoarele aspecte:
Suport pentru notiunea de interfata abstracta, distincta de implementare sau clasa;
Mecanismul de mostenire al interfetelor este aproape identic.
In concluzie, se poate afirma ca standardul CORBA este un pas important pe
drumul spre interoperabilitate si standardizarea orientata obiect. Prin intermediul CORBA, utilizatorii obtin accesul la informatie in mod transparent, fara a fi nevoiti sa cunoasca amanunte despre platforma hardware sau software ori localizarea in retea a informatiei. CORBA aduce adevarata interoperabilitate in cadrul mediului computational actual.
Directii de dezvoltare
O dezvoltare ulterioara a programuli ar putea fi realizata in urmatoarele directii:
Realizarea comunicarii, integral, cu ajutorul mecanismului producator-consumator. Acest lucru semnifica renuntarea la apelurile CORBA, adresate managerului, pentru solicitarea de servicii, informare si inregistrare. Pentru indeplinirea acestui deziderat, mecanismul de comunicare ar trebui testat si optimizat, sau inlocuit cu o varianta echivalenta, fiabila si corecta, a altei firme;
Adaugarea de noi facilitati, celor trei modalitati de comunicare: imbunatatirea editorului grafic pina la nivelul unui corespondent comercial, posibilitatea de a aduce cod executabil si prin alt protocol decit HTTP, modularizarea interfetei grafice si adaugarea de noi functionalitati si informatii despre participantii la grupul de discutii;
Realizarea interfetei IDL pentru toate componentele modulelor, lucru ce ar permite scrierea implementarilor in alte limbaje de programare;
Realizarea unei versiuni sub forma de applet. Codul ar fi, astfel, incarcat din retea si s-ar executa pe masina client. Ar trebui utilizat un serviciu de tip HTTP tunneling, care ar permite comunicarea applet-urilor, prin retea, cu serverul, in conformitate cu restrictiile de securitate specifice. Acest lucru ar putea fi realizat cu ajutorul serviciului Gatekeeper al produsului VisiBroker.
Incercind o comparatie intre programul de fata si alte implementari de grupuri de discutii se pot remarca noutatile aduse de aceasta lucrare prin utilizarea unei tehnologii de ultima ora, reprezentata de canalul de comunicatie, precum si de facilitatea ce permite incarcarea din retea a codului executabil si executia sa pe masina locala.
Bibliografie
Marian Dobre – Proiectarea sistemelor de operare – note de curs
Valentin Cristea – Sisteme de programe pentru retele de calculatoare – note de curs
Irina Athanasiu, Bogdan Costinescu, Octavian A. Dragoi, Florentina I. Popovici – Limbajul Java. O perspectiva pragmatica – Computer Libris AGORA – 1998
CORBA V2.2 – http://www.omg.org – feb. 1998
Java Remote Method Invocation Specification – http://www.javasoft.com
VisiBroker for Java v.3.3 – http://www.inprise.com/visibroker
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Grup de Discutii pe Internet (ID: 149100)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
