====== Laborator 05 ======
====== Dezvoltarea de aplicații distribuite folosind tehnologia CORBA ======
===== Obiective ====
* înţelegerea mecanismului CORBA pentru dezvoltarea unei aplicaţii distribuite, prin apelul la distanţă al metodelor unor obiecte rezidente pe server, funcţionalitate de care clientul este informat prin specificaţia în cadrul unei interfeţe independente de limbajul de programare;
* cunoașterea capabilităților oferite de serviciile de nume ce pot fi folosite pentru identificarea obiectelor la distanță într-un mediu distribuit;
* descrierea unui serviciu disponibil prin intermediul unui obiect la distanță folosind IDL;
* generarea claselor schelet (pe server) și ciot (pe client) prin intermediul compilatorului furnizat de producător;
* proiectarea și configurarea serverului care implementează metodele unui obiect la distanță;
* proiectarea și configurarea clientului care invocă metodele unui obiect la distanță.
===== Cuvinte Cheie ====
CORBA, CORBA Services, IDL, idlj, orbd, tnameserv, stub, skeleton, servant, OMG, object marshalling, IIOP
===== Materiale Ajutătoare ======
{{ :laboratoare:laborator05:aipi2014-lab05-slides.pdf }}
{{ :laboratoare:laborator05:aipi2014-lab05-slides-3pp.pdf }}
===== CORBA - aspecte generale =====
CORBA (Common Object Request Broker Architecture), dezvoltată de [[http://www.omg.org|Object Management Group]], este o tehnologie ce oferă o arhitectură independentă de platformă şi de limbajul de programare pentru a dezvolta aplicaţii distribuite, orientate pe obiecte.
Organizaţia Object Management Group, responsabilă pentru tehnologia CORBA, este formată din peste 300 de companii, cuprinzând majoritatea producătorilor de tehnologii distribuite (platforme, baze de date, aplicaţii, diferite utilitare).
Limbajul Java reprezintă o soluţie pentru dezvoltarea de aplicaţii CORBA, argumente în acest sens fiind maparea cu limbajul IDL şi sistemul de colectare a memoriei disponibile (garbage collection).
Obiectele CORBA se pot afla oriunde, comunicând prin intermediul unei reţele de calculatoare, presupunând că maşinile pe care se găsesc sunt conectate la Internet, fiind vizibile în acest spaţiu printr-o adresă publică. CORBA reprezintă astfel un standard ce permite interacţiunea între (colecţii de) obiecte distribuite (şi eterogene) în cadrul unei aplicaţii.
CORBA pune la dispoziţie un mediu pentru dezvoltarea de aplicaţii distribuite, soluţie adoptată atunci când datele folosite sunt distribuite (în baze / depozite de date aflate pe maşini diferite), când procesarea este distribuită (în sisteme de tip Grid / Cloud) sau când utilizatorii sunt distribuiţi.
Distribuirea datelor poate fi motivată prin probleme cu privire la: securitatea datelor, sau separarea datelor din raţiuni istorice (cronologice) sau al semnificaţiei.
Procesarea distribuită urmăreşte obţinerea unui avantaj competitiv prin folosirea prelucrării paralele (acolo unde poate fi realizată o astfel de descompunere în activităţi care să fie executate concomitent). Totodată, specializarea unor maşini (servere pentru baze de date, sisteme grafice) poate reprezenta un motiv pentru a recurge la distribuirea unei aplicaţii.
Caracteristicile sistemelor distribuite pot fi sistematizate astfel:
^ Criteriu ^ Sisteme Nedistribuite ^ Sisteme Distribuite ^
| comunicaţie | rapidă | lentă |
| erori | obiectele sunt distruse împreună | obiectele sunt distruse separat |
| acces concurent | doar prin fire de execuţie | da |
| securitate | da | nu |
Ţinând cont de faptul că în cadrul aceluiaşi proces comunicaţia între obiecte este semnificativ mai rapidă decât în cazul unei aplicaţii distribuite, trebuie evitată o interacţiune foarte strânsă între obiecte în cadrul proiectării aplicaţiilor ce utilizează prelucrarea paralelă.
Într-un sistem tradiţional, producerea unei erori implică distrugerea tuturor obiectelor create în cadrul procesului respectiv. Pe de altă parte, în cadrul aplicaţiilor distribuite, programatorul trebuie să ia în calcul situaţia în care disponibilitatea obiectelor este diferită (existenţa lor fiind autonomă), aceasta fiind dependentă de comportamentul procesului în care rulează.
Dacă pentru o aplicaţie nedistribuită există posibilitatea de a alege între unul sau mai multe fire de execuţie, garantând un acces secvenţial al obiectelor, respectiv implicând necesitatea folosirii unor mecanisme de sincronizare pentru controlul accesului concurent, în situaţia unui sistem ce rulează pe mai multe maşini, obiectele pot fi accesate de mai mulţi clienţi în mod implicit, astfel încât mecanismele de sincronizare trebuie implementate obligatoriu.
Securitatea este un aspect care trebuie luat în calcul atunci când obiectele sunt distribuite, interacţiunea între acestea trebuind să aibă la bază mecanisme de autentificare spre a se respecta drepturile de accesare ale acestora.
===== Arhitectura tehnologiei CORBA =====
CORBA defineşte o arhitectură pentru obiecte distribuite, bazată pe conceptul de cerere a unui serviciu pus la dispoziţie de un obiect distribuit. Serviciile pe care le oferă un obiect distribuit sunt specificate în interfaţa sa, descrisă într-un limbaj de definire al interfeţei dezvoltat de OMG (IDL – Interface Definition Language). Obiectele vor fi identificate prin referinţele la ele, având tipurile specificate în interfaţa corespunzătoare.
{{ :laboratoare:laborator05:laborator05_figura01.png?nolink&500 }}
Clientul deţine o referinţă către un obiect distribuit (rezident pe server), definit printr-o interfaţă. Serviciul (distribuit) ORB (Object Request Broker) transmite cererea obiectului distribuit pe server şi întoarce rezultatul la client. Pentru a implementa acest comportament, serviciul localizează obiectul în reţea, comunică cererea către acesta, aşteptă rezultatul, pentru ca în momentul în care este disponibil să îl transmită către aplicaţia care l-a solicitat. Aşadar, ORB realizează transparenţa atât faţă de **locaţia** unde se află implementat obiectul care pune la dispoziţie serviciile cât şi faţă de **limbajul de programare** în care se scrie cererea (acesta putând fi diferit faţă de limbajul de programare în care este implementat serviciul obiectului), realizând corespondenţa necesară.
Specificaţia CORBA are drept obiectiv portabilitatea atât a clientului cât şi a implementării obiectelor. În acest sens, este definit un API atât pentru clienţii unui obiect distribuit cât şi pentru implementarea obiectului CORBA. Acest obiectiv ar presupune că se poate rescrie cu uşurinţă codul dezvoltat pentru accesarea unui serviciu CORBA al unui producător, astfel încât să poată funcţiona în alte contexte similare. În realitate, aplicaţii de tip client sunt portabile, însă adaptarea în cazul implementărilor obiectelor este mai dificilă pentru a asigura interoperabilitatea cu mai multe produse CORBA.
Pentru asigurarea interoperabilităţii, este definit un protocol IIOP (Internet Inter-ORB Protocol) ce permite clienţilor folosind un produs CORBA realizat de un producător să poată comunica cu obiecte din cadrul altui produs CORBA dezvoltat de orice alt producător. IIOP funcţionează peste orice implementare TCP/IP.
În cadrul unui sistem distribuit, interoperabilitatea este mai importantă decât portabilitatea. Astfel, IIOP este folosit şi în alte sisteme decât în cele care implementează API-ul CORBA. Deoarece toate aceste sisteme folosesc IIOP ca protocol de transport, deşi utilizează API-uri diferite, pot interacţiona între ele.
IIOP este folosit ca protocol de transport pentru o versiune a Java RMI (RMI peste IIOP). Enterprise Java Beans pot folosi IIOP (deoarece sunt definite prin intermediul RMI). Totodată, numeroase servere de aplicaţii utilizează IIOP fără a implementa întreg API-ul CORBA.
Un aspect important în arhitectura CORBA este reprezentat de definirea unui set de servicii distribuite, cunoscute sub numele de COS (CORBA Services), care suportă integrarea şi interacţiunea dintre obiecte distribuite. Ele sunt implementate ca obiecte distribuite CORBA, definite prin interfeţe IDL.
{{ :laboratoare:laborator05:laborator05_figura02.png?nolink&500 }}
^ Serviciu ^ Descriere ^
| ciclu de viaţă al obiectului | defineşte procesele de creare, ştergere, mutare şi copiere a obiectelor CORBA |
| serviciu de nume | defineşte modul în care se pot asocia obiectelor CORBA nume simbolice |
| serviciu de evenimente | decuplează comunicarea între obiecte CORBA |
| serviciu de legături | stabileşte legături (cu multiplicităţi şi tipuri asociate) între obiecte CORBA |
| serviciu de externalizare | realizează transformarea obiectelor CORBA în / din surse externe |
| serviciu de tranzacţii | coordonează atomicitatea accesului la obiecte CORBA |
| control al accesului concurent | oferă servicii de blocare a accesului la obiecte CORBA pentru asigurarea mecanismului de serializare |
| serviciu de proprietăţi | stabileşte asocierea perechilor de tip nume-valoare cu obiecte CORBA |
| serviciu de identificare | găseşte obiecte CORBA pe baza proprietăţilor care descriu serviciul oferit |
| serviciu de interogări | permite realizarea de interogări cu obiecte CORBA |
CORBA este o specificaţie implementată de mai mulţi producători pentru diferite limbaje de programare. Pentru Java, au fost dezvoltate Java ORB (distribuită împreună cu SDK-ul Java, având unele funcţionalităţi lipsă), VisiBroker for Java (dezvoltată de Borland), Orbix Web (de la Iona Technologies), WebSphere (server de aplicaţii realizat de IBM).
==== Exemplu ====
În cele ce urmează, implementarea unei aplicaţii distribuite folosind tehnologia CORBA va fi ilustrată pe cazul particular al aplicaţii implementând un serviciu de rezervare, a cărui funcţionalitate a fost descrisă pe larg în laboratorul anterior.
===== Specificarea funcționalității unui obiect distribuit în IDL =====
Prin limbajul de definire a interfeţelor dezvoltat în cadrul OMG (IDL – Interface Definition Language) se permite specificarea obiectelor distribuite prin operaţiile suportate, fără a se oferi detalii cu privire la modul în care acestea sunt realizate. Prin urmare, nu se vor specifica în IDL stări ale obiectului sau algoritmi. Implementarea unui obiect CORBA este realizată într-un limbaj de programare, interfaţa fiind doar o punte de legătură (un contract) între codul care foloseşte obiectul (dependent doar de interfață) şi codul care defineşte obiectul. Interfaţa poate fi considerată un contract în sensul garantării unei anumite funcţionalităţi (prin parametrii de intrare şi de ieşire) fără a oferi detalii despre modul de implementare.
Interfaţa în care este specificat comportamentul obiectului distribuit este independentă de limbajul de programare. IDL defineşte corespondenţe pentru mai multe limbaje de programare, permiţând deci ca implementarea unui obiect să se poată face în limbajul de programare adecvat pentru obiectul respectiv. Totodată, acelaşi lucru este posibil şi pentru client.
Au fost standardizate corespondenţe între IDL şi C, C++, Java, Ada, COBOL, SmallTalk, Objective C, Lisp.
Pot fi definite astfel, independent de limbajul de programare, interfeţe modularizate pentru obiecte, specificând atribute şi metode suportate de acestea. De asemenea, pot fi indicate excepţiile pe care le poate genera o anumită operaţie. Fiecare operaţie este definită prin tipul de date al valorii întoarse precum şi prin tipurile de date ale parametrilor.
IDL suportă tipuri de date de bază (''boolean'', ''char'', ''octet'', ''short'', ''long'', ''float'', ''double'', ''string'') sau construite (''struct'', ''union'', ''enum'', ''sequence''), obiecte definite sau tipul ''any'' pentru obiecte cu tipul de date stabilit dinamic (la momentul utilizării).
Corespondenţa între tipuri de date IDL şi cele din limbajul de programare Java sunt descrise mai jos:
^ IDL ^ Java ^
| ''boolean'' | ''boolean'' |
| ''char'' / ''wchar'' | ''char'' |
| ''octet'' | ''byte'' |
| ''short'' / ''unsigned short'' | ''short'' |
| ''long'' / ''unsigned long'' | ''int'' |
| ''long long'' / ''unsigned long long'' | ''long'' |
| ''float'' | ''float'' |
| ''double'' | ''double'' |
| ''string'' / ''wstring'' | ''String'' |
De asemenea, există o corespondenţă între anumite structuri IDL şi structuri Java.
^ IDL ^ Java ^
| ''module'' | ''package'' |
| ''interface'' | ''interface'' |
| ''operation'' | method |
| ''attribute'' | pair of methods |
| ''exception'' | ''exception'' |
Un concept întâlnit frecvent atunci când se vorbeşte despre corespondenţa tipurilor de date este cel de tipare inversă (//eng.// marshalling), adică de transformare a unei structuri specifice unui limbaj de programare într-un format CORBA IIOP, astfel încât să poată fi transmisă prin reţea.
Pentru aplicaţia considerată, un exemplu de specificaţie IDL poate fi:
module Reservation
{
struct Date
{
long day;
long month;
long year;
};
struct Moment
{
long hour;
long minute;
};
struct Time
{
Date date;
Moment moment;
};
struct Interval
{
Time start;
Time end;
};
typedef sequence Intervals;
exception UnspecifiedTimeTable {};
interface ReservationService {
Intervals getTimeTable() raises(UnspecifiedTimeTable);
long getAvailableSeats(in Interval interval) raises(UnspecifiedTimeTable);
boolean makeReservation(in long customerId, in Interval interval, in Interval numberOfSeats) raises(UnspecifiedTimeTable);
boolean cancelReservation (in long customerId, in Interval interval) raises(UnspecifiedTimeTable);
};
};
În specificaţia IDL ''Reservation.idl'', au fost definite ca structuri de date ''Date'', ''Moment'', ''Time'', ''Interval'', ''Intervals'', şi excepţia ''UnspecifiedTimeTable'', utilizate ulterior în interfaţa ''ReservationService''. Au fost definite mai multe metode ce primesc parametri de intrare (fapt specificat prin cuvântul cheie ''in''), descriind funcţionalitatea oferită de aplicaţie. Parametrii de ieşire (indicând date care sunt transmise de la obiectul distribuit către client) sunt specificaţi prin cuvântul cheie ''out'', iar parametrii de intrare ieşire (indicând date transmise de la client către obiectul distribuit şi înapoi la client) sunt specificaţi prin cuvântul cheie ''inout''.
Produsele CORBA pun la dispoziţie un compilator care converteşte interfaţa IDL în reprezentarea corespunzătoare limbajului de programare. Pentru distribuţia standard Java, acesta se numeşte ''idlj'' și se găseşte în directorul ''bin'' al distribuţiei standard.
Prin comanda
idlj Reservation.idl
vor fi generate următoarele fişiere:
^ Fişier ^ Semnificaţie ^
| ''ReservationService.java'' | Interfaţa IDL reprezentată ca intefaţă Java |
| ''ReservationServiceHelper.java'' | Implementează operaţiile de tipare pentru interfaţă (''insert'', ''extract'', ''read'', ''write'', ''narrow'', ''unchecked_narrow'') |
| ''ReservationServiceHolder.java'' | E folosit pentru operaţii cu parametri de tip ''out'' şi ''inout''. |
| ''ReservationServiceOperations.java'' | Conţine descrierile operaţiilor implementate de interfaţă (''ReservationService'' este derivată din ''ReservationServiceOperations'') |
| ''_ReservationServiceStub.java'' | Implementează un obiect local reprezentând obiectul CORBA "la distanţă" ce transmite cererile spre obiectul distribuit |
Au fost menţionate doar fişierele cu semnificaţie în dezvoltarea aplicaţiei distribuite, fiind omise fişierele corespunzătoare celorlalte structuri de date, al căror format (ca denumire şi funcţionalitate) este asemănător.
Pentru a genera atât fişierele pentru client şi pentru server, utilitarul idlj este apelat cu parametrul ''-fall''. Pentru a genera doar fişierele pentru server sau pentru client sunt folosiţi parametrii ''-fserver'', respectiv ''-fclient''.
Implicit (dacă utilitarul este apelat fără parametri), sunt generate doar fişierele pentru client.
===== Etapele dezvoltării unei aplicaţii distribuite folosind CORBA =====
Aşadar, etapele dezvoltării unei aplicaţii distribuite folosind CORBA sunt:
* definirea interfeţei care stabileşte funcţionalitatea obiectului distribuit, folosind limbajul de specificare IDL, astfel încât programatorii să poată implementa programe client şi server în orice limbaj de programare compatibil cu CORBA (când se implementează un server sau un client, interfeţele IDL sunt primite de la producător);
* compilarea interfeţei care conţine funcţionalitatea obiectului distribuit (pentru Java, se foloseşte ''idlj''); se obţine versiunea interfeţei corespunzătoare limbajului de programare respectiv, şi totodată clasele ciot (//eng.// stub) respectiv schelet (//eng.// skeleton) care conţin infrastructura de conectare la ORB;
* dezvoltarea serverului (se folosesc clasele schelet generate); pe lângă implementarea metodelor specificate în interfaţă, trebuie precizat mecanismul de pornire a serviciului ORB şi de aşteptare a invocărilor realizate de client;
* dezvoltarea clientului (se folosesc clasele ciot generate); clientul porneşte serviciul ORB, caută serverul folosind serviciul de nume oferit de interfaţă pentru a obţine o referinţă către obiectul distribuit, spre a-i apela metodele
* pornirea aplicaţiilor în următoarea ordine: serviciu de nume, server, client.
Prin serviciul de nume (referinţele către) obiectele distribuite sunt publicate de server, devenind astfel disponibile clienţilor, care le pot folosi pentru a apela metodele specificate în interfaţă. Se va utiliza ''orbd'', un proces care rulează în fundal (//eng.// daemon) şi conţine serviciile: de iniţializare (Bootstrap Service), de nume tranzitoriu (Transient Name Service) dar şi de nume permanent (Persistent Name Service) şi un proces de gestiune pentru server (Server Manager).
Se poate folosi serviciul de nume (tranzitoriu) ''tnameserv'' (apelat fără parametri), ca alternativă la ''orbd''. De regulă, ''tnameserv'' se lansează pe portul 900.
Într-un sistem Unix, serviciul de nume se porneşte astfel:
aipi2014@ubuntu:$ orbd -ORBInitialPort 1100&
Într-un sistem Windows, serviciul de nume se porneşte astfel:
C:\Users\Aipi2014> start orbd –ORBInitialPort 1100
Parametrul ''ORBInitialPort'' este obligatoriu.
CORBA implementează un sistem de excepţii care este foarte similar celui din limbajul de programare Java.
Există două tipuri de excepţii în CORBA: **sistem** şi **utilizator**.
Excepţiile de tip sistem sunt generate atunci când au loc evenimente ce ţin de aplicaţie la modul general (a fost apelată o metodă care nu este implementată pe server, există o problemă de comunicare, sistemul ORB nu a fost iniţializat corespunzător). //System Exceptions// în Java sunt o subclasă a ''RuntimeException'' astfel încât ele nu trebuie incluse într-un bloc ''try {...} catch''. Totuşi, tratarea excepţiilor în acest fel poate beneficia de avantajul obţinerii unui cod de eroare, care diferă de la producător la producător.
Excepţiile de tip utilizator sunt generate în cazul unor erori în cadrul unei metode propriu-zise. //User Exceptions// în Java sunt o subclasă a ''Exception'', aşadar trebuie incluse (obligatoriu) într-un bloc ''try {...} catch''.
==== Proiectarea serverului ====
Implementarea obiectului distribuit CORBA este transparentă pentru client, care cunoaşte doar specificaţiile din interfaţa IDL. Încapsularea completă a obiectelor distribuite CORBA oferă mai multă libertate în ceea ce priveşte implementarea lor, aceasta putând fi realizată în limbaje de programare diferite chiar şi faţă de limbajul de programare în care este scris clientul. La nivel de server, implementarea obiectului distribuit se face pe baza clasei schelet generată la compilarea interfeţei IDL. Se oferă astfel mecanismul de acces la nivelul obiectului distribuit, ce conţine despachetarea datelor primite, apelarea metodei ce implementează operaţia solicitată, împachetarea rezultatelor transmise.
Pentru implementarea obiectului distribuit CORBA, programatorul trebuie să dezvolte o clasă derivată din casa schelet, specificând comportamentul pentru metodele descrise de interfaţa IDL.
CORBA specifică două mecanisme de dezvoltare a serverului pentru implementarea unei interfeţe IDL:
* modelul moştenirii, care presupune implementarea unei clase care extinde clasa schelet generată de compilator; din acest model fac parte:
* standardul POA (Portable Object Adapter), dezvoltat de OMG; compilatorul generează o clasă ''*POA.java'' care extinde ''org.omg.PortableServer.Servant'', folosită drept clasă de bază pentru toate implementările servantului (clasa care implementează metodele specificate în interfaţa IDL pe server);
Un obiect de adaptare (//eng.// object adapter) este un mecanism care conectează o cerere la codul care o deserveşte prin intermediul unui obiect referinţă. POA este un tip particular al acestui tip de obiect definit de specificaţia CORBA care îndeplineşte următoarele obiective: permite programatorilor să dezvolte implementări de obiecte care sunt portabile între diferite produse ORB, oferă suport pentru obiecte persistente (din perspectiva clientului, obiectele există pe tot parcursul aplicaţiei menţinând valorile stocate în ele – deşi serverul poate fi repornit sau implementările pot fi realizate prin obiecte diferite), pentru activarea transparentă a obiectelor, permiţând ca un servant să suporte simultan mai multe identităţi de obiecte. Prin POA, implementarea deţine controlul asupra identităţii, stării, stocării, ciclului de viaţă al obiectului.
* ImplBase – este folosit doar pentru a asigura compatibilitatea cu servere scrise în versiuni Java mai vechi (< 1.4); este recomandată evitarea acestui model în favoarea POA.
* modelul delegării – interfaţa IDL este implementată folosind două clase
* o clasă generată de compilator care moşteneşte clasa schelet, dar deleagă toate apelurile unei clase care implementează metodele;
* o clasă care implementează operaţiile generate din interfaţa IDL (având denumirea ''*Operations.java'').
La nivelul serverului, trebuie realizate următoarele operaţii:
- crearea şi iniţializarea instanţei ORB;
- obţinerea unei referinţe către obiectul rădăcină POA şi activarea POAManager;
- instanţierea servantului (implementarea obiectului distribuit);
- obţinerea unei referinţe către contextul de nume în care se va înregistra obiectul distribuit (obţinerea obiectului rădăcină al contextului de nume);
- înregistrarea obiectului distribuit în contextul de nume;
- aşteptarea invocărilor obiectului distribuit de către client.
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import org.omg.PortableServer.*;
import Reservation.*;
public class Server {
public static void main (String[] args) {
try {
ORB orb = ORB.init(args,null);
POA POARoot = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
POARoot.the_POAManager().activate();
ReservationServiceImplementation ReservationServiceImpl = new ReservationServiceImplementation();
org.omg.CORBA.Object ReservationServiceImplRef = POARoot.servant_to_reference(ReservationServiceImpl);
ReservationService ReservationServiceRef = ReservationServiceHelper.narrow(ReservationServiceImplRef);
org.omg.CORBA.Object nameServiceRef = orb.resolve_initial_references("NameService");
NamingContextExt nameContextRef = NamingContextExtHelper.narrow(nameServiceRef);
String serviceName = "ReservationService";
NameComponent nameComponent = new NameComponent(serviceName,"");
NameComponent path[] = { nameComponent };
nameContextRef.rebind(path,ReservationServiceRef);
orb.run();
} catch (Exception exception) {
System.out.println ("An exception has occurred: "+exception.getMessage());
}
}
}
Clasa ''ReservationServiceImplementation'' reprezintă servantul, care extinde clasa ''ReservationServicePOA'', astfel încât moşteneşte funcţionalitatea CORBA generată de compilatorul IDL. Aceasta va conţine toate implementările descrise prin interfaţă, conţinând serviciile puse la dispoziţie.
Un server CORBA are nevoie de un obiect ORB local, ca de altfel şi fiecare client CORBA. Toate obiectele servant vor fi înregistrate prin acesta, astfel încât serviciul ORB să poată localiza serverul atunci când sunt invocate funcţionalităţi pe care le pune la dispoziţie. Prin metoda ''init()'', ce primeşte argumente din linia de comandă, se pot specifica anumite proprietăţi, între care cele mai importante sunt portul şi adresa pe care rulează serverul.
Serverul va fi lansat în execuţie astfel:
* Linux
aipi2014@ubuntu:$ java Server -ORBInitialPort 1100 -ORBInitialHost localhost&
* Windows
C:\Users\Aipi2014> start java Server -ORBInitialPort 1100 -ORBInitialHost localhost
Apoi, prin intermediul serviciului ORB se obţine obiectul care conţine referinţa către obiectul POA rădăcină prin metoda ''resolve_initial_references()''. După obţinerea referinţei către obiectul POA rădăcină se activează serviciul ''POAManager''. Prin metoda ''activate()'' se schimbă starea serviciului de gestiune POA în activ astfel încât obiectele asociate acestuia vor prelucra cererile venite de la client. Fiecare obiect ''POA'' are asociat un obiect ''POAManager'' (care poate gestiona, la rândul lui, unul sau mai multe obiecte ''POA''). Prin ''POAManager'' se încapsulează starea în care se găseşte prelucrarea fiecărui obiect POA asociat.
Serverul este procesul care instanţiază unul sau mai multe obiecte servant. Un servant moşteneşte interfaţa generată de compilatorul IDL, implementând propriu-zis prelucrările specificate prin operaţiile respective. Servantul ''ReservationServiceImplementation'' este instanţiat prin obiectul ''ReservationServiceImpl''.
Se obţine o referinţă către obiectul distribuit, iniţial ca obiect CORBA (prin metoda ''servant_to_reference()'', apelată din contextul obiectului rădăcină POA, instanţei servantului), cast-ul la tipul corespunzător făcându-se prin metoda (statică) ''narrow()'' din clasa (generată de compilatorul IDL) ''ReservationServiceHelper''.
Operaţiile puse la dispoziţie de (instanţa) servant(ului) sunt făcute vizibile către client prin serviciul de nume Common Object Service (COS).
Obiectele pot fi făcute vizibile către client şi prin transformarea într-un şir de caractere a referinţelor către acestea şi publicarea lor într-un fişier.
Se obţine referinţa către acest serviciu de nume astfel încât să se poată înregistra prin ea referinţele către obiectele care implementează diferite interfeţe. Referinţele către obiectele care implementează diferite interfeţe sunt folosite de clienţi pentru invocarea metodelor specificate în interfaţă.
Referinţa către serviciul de nume se face tot prin metoda ''resolve_initial_references()'', transformarea către tipul corespunzător contextului de nume realizându-se prin metoda ''narrow()'', la fel ca în cazul obiectelor POA. Parametrul "NameService" este definit pentru toate serviciile ORB, indicând faptul că serviciul de nume va fi persistent, dacă se foloseşte serviciul de nume ''orbd'', respectiv tranzitoriu, în cazul utilizării ''tnameserv''.
Se realizează o componentă de nume indicându-se denumirea prin care serviciile puse la dispoziţie de obiectul respectiv pot fi accesate. Se face apoi legătura dintre componenta de nume şi referinţa către obiectul distribuit, întoarsă către client prin apeluri tip ''NamingContext.resolve_str(serviceName)'', prin care se poate obţine acces către funcţionalitatea dorită.
Apelul ''orb.run()'' blochează serverul în aşteptarea invocărilor realizate din contextul clientului. Deoarece apelul este făcut din metoda ''main()'', este folosit firul de execuţie corespunzător acesteia, astfel încât, la momentul încheierii prelucrărilor corespunzătoare unei invocări, serverul va rămâne în continuare blocat în aşteptarea unor alte apeluri. Datorită acestui comportament, trebuie specificată o condiţie de terminare, implicând apelul metodei ''shutdown()'' asociată obiectului corespunzător serviciului ORB.
==== Proiectarea clientului ====
Operaţiile realizate la nivelul clientului sunt asemănătoare cu cele realizate de către server:
* crearea şi iniţializarea instanţei ORB – se foloseşte metoda init() la fel ca în cazul serverului, iar parametrii (din linia de comandă) indică, similar, portul şi adresa unde rulează serviciul de nume;
java Client -ORBInitialPort 1100 -ORBInitialHost localhost
Orice client are nevoie de un obiect local ORB pentru realizarea împachetării datelor transmise şi prelucrărilor IIOP.
* obţinerea unei referinţe către contextul de nume folosit pentru identificarea (unei referinţe către) obiectul(ui) distribuit – este apelată metoda ''resolve_initial_references()'' din contextul obiectului ORB având parametrul "NameService", cu aceeaşi semnificaţie ca în cazul serverului, transformarea la tipul dorit realizându-se prin metoda narrow;
* utilizarea serviciului de nume pentru căutarea obiectului distribuit prin specificarea unei denumiri sub care aceasta a fost înregistrat (de către serverul pe care se găseşte o implementare a sa) – se foloseşte metoda ''resolve_str()'' a obiectului care conţine o referinţă contexul de nume, având ca parametru denumirea sub care a fost specificat, în acelaşi mod precum s-a realizat localizarea serviciului de nume;
* transformarea (referinţei către) obiectul(ui) distribuit CORBA obţinut la tipul corespunzător folosindu-se metoda ''narrow()'';
* apeluri ale metodelor puse la dispoziţie de obiectul distribuit CORBA, specificate în interfaţa IDL – invocările CORBA arată la fel ca apelul unei metode realizată asupra unui obiect local, detaliile cu privire la împachetare şi transmitere fiind transparente către utilizator.
import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import Reservation.*;
public class Client {
public static void main (String[] args) {
try {
ORB orb = ORB.init(args,null);
org.omg.CORBA.Object nameServiceRef = orb.resolve_initial_references("NameService");
NamingContextExt nameContextRef = NamingContextExtHelper.narrow(nameServiceRef);
String serviceName = "ReservationService";
ReservationService ReservationServiceRef = ReservationServiceHelper.narrow(nameContextRef.resolve_str(serviceName));
// TO DO: apeleaza metode obiect rezervare
} catch (Exception exception) {
System.out.println ("An exception has occurred: "+exception.getMessage());
}
}
}
===== Comparaţie între modelele de programare CORBA implementate în Java =====
IIOP este un protocol de transport (peste Internet) între obiecte tip ORB având drept scop realizarea interoperabilităţii între limbaje de programare şi aplicaţii comercializate de diverşi producători. Acest protocol este utilizat pentru sisteme distribuite dezvoltate fie în Java RMI, fie în IDL.
Modelul de programare IDL este axat pe interfaţa care descrie metodele care pot fi apelate din procesul "la distanţă", specificând tipurile de argumente acceptate de procedura invocată cât şi tipurile informaţiilor întoarse de aceasta. CORBA este un sistem independent de limbajul de programare în care valorile argumentelor sau rezultatelor întoarse sunt limitate la ceea ce poate fi reprezentat în limbajele de programare în care se realizeaza implementarea. Tipurile parametrilor de intrare şi de ieşiure trebuie să fie cele specificate în interfaţă. Totodată, orientarea pe obiecte este limitată la obiecte transmise prin referinţă (obiectul nu poate fi transmis între maşini prin cod).
Modelul de programare IDL (JavaTM IDL) conţine Java CORBA ORB şi compilatorul ''idlj'' – care realizează corespondenţa între limbajele IDL şi Java. Astfel, este implementată funcţionalitatea CORBA în platforma Java, oferind interoperabilitate şi conectivitate standardizată permiţând aplicaţiilor distribuite să invoce – în mod transparent – operaţii puse la dispoziţie prin reţea folosind IDL şi IIOP. Este folosit un obiect ORB pentru prelucrări distribuite folosind comunicaţie bazată pe IIOP. Folosirea acestui model implică definirea intefeţelor "la distanţă" folosind limbajul IDL (dezvoltat de OMG), compilarea lor folosind ''idlj'' care va genera versiunea Java a interfeţei ca şi clasele ciot şi schelet care permit aplicaţiilor să comunice prin ORB. Java IDL este o componentă a distribuţiei standard Java.
Limbajul IDL de specificare a intefeţelor este pur declarativ, proiectat pentru specificarea operaţiilor corespunzătoare unei aplicaţii distribuite independent de limbajul de programare. El cuprinde şi corespondenţele către limbaje de programare ca Java, C, C++, Lisp, Python, Smalltalk, COBOL şi Ada, astfel că fiecare instrucţiune este translatată corespunzător. Astfel, un client dezvoltat în Java se poate folosi de implementările (specificate ca interfaţă IDL) realizate în C++ pentru un anumit serviciu, interoperabilitatea dintre cele două limbaje de programare fiind obţinută prin intermediul ORB.
În RMI, interfaţa şi implementarea ei sunt dezvoltate folosind acelaşi limbaj de programare, astfel încât nu există problema corespondenţei dintre acestea. Obiecte la nivel de limbaj de programare (codul însuşi) pot fi transmise de la un proces la altul.
Modelul de programare RMI se referă la prelucrări distribuite prin API-ul cu acelaşi nume. Se poate lucra doar în limbajul de programare Java, folosind Java Remote Method Protocol (JRMP) sau cu limbaje de programare compatibile CORBA, folosind protocolul IIOP. Acest model de programare este parte din distribuţia standard Java şi conţine serviciul ORB şi compilatorul ''rmic'' pentru generarea claselor ciot şi schelet precum şi a legăturilor dintre obiecte la distanţă folosind protocoalele JRMP sau IIOP. Folosind acelaşi limbaj de programare, dezvoltarea este mai rapidă, iar flexibilitatea este obţinută prin posibilitatea transmiterii de obiecte serializabile.
Modelul de programare IDL sau RMI cu componenta IIOP vor fi folosite aşadar doar pentru interfaţarea cu anumite aplicaţii (//eng.// legacy systems), preferându-se folosirea modelului de programare RMI din motive ce ţin de portabilitate, securitate şi colectarea memoriei disponibile.
===== Activitate de Laborator =====
{{ :laboratoare:laborator05:aipi2014-lab05-scripts.zip }}
{{ :laboratoare:laborator05:aipi2014-lab05-remoteinterfaces.zip }}
{{ :laboratoare:laborator05:aipi2014-lab05-eclipse.zip }}
{{ :laboratoare:laborator05:aipi2014-lab05-netbeans.zip }}
**1.** [**0 puncte**] Să se pornească serviciul de nume orbd prin apelarea scripturilor ''startNameService''[''.bat''|''.sh''].
În cazul când calea către directorul bin al SDK-ului Java nu este precizată în variabila de mediu ''PATH'', acest script nu va fi executat cu succes.
În cazul când calea către directorul bin al SDK-ului Java nu este precizată în variabila de mediu ''PATH'', acest script nu va fi executat cu succes.
Precizarea căii către directorul ''bin'' al SDK-ului Java în variabila de mediu ''PATH'' se realizează astfel:
* Linux
export PATH=${PATH}:/usr/lib/jvm/default-java/bin
* Windows Computer Properties → Control Panel Home/Advanced System Properties → System Properties/Advanced/Environment Variables → System Variables / Path / Edit
SET PATH=%PATH%;C:\Program Files\Java\jdk1.8.0_25\bin
\\ {{ :laboratoare:laborator04:windows7_01.png?nolink }}\\ {{ :laboratoare:laborator04:windows7_02.png?nolink&400 }}\\ {{ :laboratoare:laborator04:windows7_03.png?nolink&400 }}\\ {{ :laboratoare:laborator04:windows7_04.png?nolink&400 }}
Verificaţi că procesul ''orbd'' rulează înainte de a porni serverul.
* Linux
aipi2014@ubuntu:~$ ps -a
* Windows - procesul apare în lista Task Manager
**2.** [**0 puncte**] Să se compileze intefaţa ''Reservation.idl'' folosind compilatorul ''idlj'' pentru generarea claselor server, respectiv client, plasându-se sursele generate în directoare diferite.
* server
idlj -fserver Reservation.idl
* client
idlj -fclient Reservation.idl
Se observă faptul că la generarea claselor din server lipsesc fişierele ''ReservationServiceHelper.java'' şi ''_ReservationServiceStub.java''. Acestea vor trebui preluate din clasele generate pentru client.
Să se includă fișierele generate în codul sursă corespunzător proiectelor pentru server, respectiv client.
Folosind mediul integrat de dezvoltare (Refactor → Rename), să se redenumească pachetul ''reservation'' în ''ro.pub.cs.aipi.lab05.reservation''.
Să se creeze configurații de rulare pentru server, respectiv client (dacă nu se folosesc testele unitare), specificând parametrii liniei de comandă:
* ''-ORBInitialHost'' având valoarea ''localhost'';
* ''-ORBInitialPort'' având valoarea ''1100''.
Astfel, valoarea parametrilor din linia de comandă va fi:
-ORBInitialHost localhost -ORBInitialPort 1100
**3.** [**10 puncte**] Să se încarce orarul de funcţionare al restaurantului în constructorul clasei ''ReservationServiceImplementation'' cu datele conţinute de fişierul ''timetable.txt''.
Se va parsa conţinutul fişierului ''timetable.txt'' care pe prima linie conţine capacitatea (numărul de locuri) încărcată în variabila ''numberOfSeats'' iar pe următoarele linii programul pentru fiecare zi calendaristică exprimat sub forma ''ZZ LL AAAA O1 M1 O2 M2'', acesta fiind încărcat în obiectul ''timetable''.
Să se implementeze metoda getTimetable a clasei ''ReservationServiceImplementation''.
Această metodă întoarce conţinutul obiectului ''timetable''.
Pentru a reutiliza codul implementat în laboratorul anterior, ar putea fi utilă implementarea unor metode de conversie între clasele ''Interval'' / ''ReservationData'' deja definite (existente în pachetul ''entities'') şi clasele ''Interval'' / ''ReservationInformation'' generate din interfaţa IDL (existente în pachetul ''reservation'').
Pentru clasa ''Interval'' metodele de conversie trebuie realizate in ambele sensuri.
public ro.pub.cs.aipi.lab05.reservation.Interval entitiesIntervalToReservationInterval(ro.pub.cs.aipi.lab05.entities.Interval interval) {
ro.pub.cs.aipi.lab05.reservation.Interval result = new ro.pub.cs.aipi.lab05.reservation.Interval();
// TO DO
return result;
}
public ro.pub.cs.aipi.lab05.entities.Interval reservationIntervalToEntitiesInterval(ro.pub.cs.aipi.lab05.reservation.Interval interval) {
ro.pub.cs.aipi.lab05.entities.Interval result = new ro.pub.cs.aipi.lab05.entities.Interval();
// TO DO
return result;
}
Pentru clasa ''ReservationData'' / ''ReservationInterval'', conversia trebuie realizată numai în sensul ''ReservationData'' → ''ReservationInterval''.
public ro.pub.cs.aipi.lab05.reservation.ReservationInformation entitiesReservationDataToReservationReservationInformation(ro.pub.cs.aipi.lab05.entities.ReservationData reservationData) {
ro.pub.cs.aipi.lab05.reservation.ReservationInformation result = new ro.pub.cs.aipi.lab05.reservation.ReservationInformation();
// TO DO
return result;
}
**4.** [**40 puncte**] Să se implementeze metoda ''getAvailableSeats()'' din cadrul clasei ''ReservationServiceImplementation''.
**5.** [**10 puncte**] Să se implementeze metoda ''makeReservation()'' din cadrul clasei ''ReservationServiceImplementation''.
**6.** [**10 puncte**] Să se implementeze metoda ''cancelReservation()'' din cadrul clasei ''ReservationServiceImplementation''.
**7.** [**10 puncte**] Să se apeleze metodele ''makeReservation()'' şi ''cancelReservation()'' din cadrul clasei ''Client''. Informaţiile utilizate ca parametrii ai metodelor se citesc de la tastatură sau pot fi definite sub formă de constante în codul sursă.
**8.** [**10 puncte**] Verificaţi comportamentul la distanţă al aplicaţiei, apelând metodele implementate de colegii voştri.
Pentru ca serverul implementat de colegii voştri să fie vizibil către clientul cu care doriţi să îl accesaţi, componentele trebuie să aibă adrese IP publice sau să se găsească în aceeaşi reţea de calculatoare în cadrul căreia să aibă adrese IP private din acelaşi spaţiu (mască de reţea / gateway).
Acest exerciţiu nu va fi punctat decât în cadrul laboratorului.
**9.** [**20 puncte**] Să se definească în interfaţa IDL o metodă
Reservations getReservation(in long customerId)
care să întoarcă lista cu intervalele în care clientul respectiv are făcute rezervări, precum şi numărul de locuri rezervate în fiecare dintre cazuri.
Să se implementeze metoda în clasa ''ReservationServiceImplementations''.
În interfaţa IDL, o posibilă definiţie a structurii ''Reservations'' ar putea fi:
struct ReservationInformation
{
long customerId;
Interval interval;
long numberOfSeats;
};
typedef sequence Reservations;
Ulterior definirii metodei în interfața IDL, vor trebui generate din nou clasele ciot și schelet pentru client, respectiv server (se vor urmări etapele de la punctul 2).
===== Resurse ======
[[http://www.corba.org/|CORBA Web Site]]\\
[[http://www.omg.org/spec/CORBA/|OMG Formally Released Versions of CORBA®]]\\
[[https://docs.oracle.com/javase/tutorial/rmi/index.html|The Java™ Tutorials - Trail: RMI]]\\
[[http://www.javaworld.com/article/2077037/soa/corba-meets-java.html|CORBA meets Java]]\\
[[http://www.oracle.com/technetwork/java/javase/tech/corba-135898.html|Java SE Core Technologies - CORBA / RMI-IIOP]]\\
[[http://www.oracle.com/technetwork/java/idl-139708.html|Java IDL Code Samples]]\\
[[http://dsnet.tu-plovdiv.bg/website/container/corba/corba.html|Introduction to CORBA - Short Course]]
===== Soluții =====
[[https://github.com/aipi2014/Laborator05/tree/master/Eclipse/|Proiect Eclipse]]\\
[[https://github.com/aipi2014/Laborator05/tree/master/NetBeans/|Proiect NetBeans]]