Laborator 03

Proiectarea de aplicații distribuite folosind RMI (Remote Method Invocation)

Obiective

  • înţelegerea mecanismului RMI pentru dezvoltarea unei aplicaţii distribuite, prin apelul la distanţă al metodelor unor obiecte rezidente pe server (într-o mașină virtuală Java diferită), funcţionalitate de care clientul este informat prin specificaţia în cadrul unei interfeţe care conține semnăturile metodelor ce pot fi invocate;
  • cunoașterea capabilităților oferite de serviciul de nume RMI (rmiregistry) utilizat pentru identificarea obiectelor la distanță într-un mediu distribuit;
  • descrierea unui serviciu disponibil la distanță prin definiții de metode în cadrul unei interfețe;
  • specificarea unei politici de securitate atât pe server cât și pe client pentru a indica drepturile unui cod sursă rezident pe mașina virtuală Java respectivă / transmis prin transferul de comportament;
  • precizarea configurațiilor de rulare necesare pentru lansarea în execuție a serverului și a clientului;
  • proiectarea și configurarea serverului care implementează metodele unui obiect la distanță;
  • proiectarea și configurarea clientului care invocă metodele unui obiect la distanță.

Cuvinte Cheie

Remote Method Invocation, stub, skeleton, proxy, garbage collection, remote procedure call, serialization, transient, rmiregistry, security manager

Materiale Ajutătoare

TODO

RMI - aspecte generale

RMI - Remote Method Invocation este un mecanism care extinde funcţionalitatea RPC, permiţând invocarea unei metode a unui obiect care există într-un alt spaţiu de adresă, în contextul unei mașini virtuale diferite.

RPC - Remote Procedure Call reprezintă tehnologia pentru apelul unei metode la distanţă, mecanism din care RMI a fost inspirat, extinzând funcţionalitatea pe care acesta o pune la dispoziţie prin orientarea acesteia pe obiecte.

RMI implementează comunicaţia la distanţă doar între programe scrise în limbajul de programare Java.

Obiectivul RMI s-a concentrat pe a pune la dispoziţia programatorilor mijloacele de a dezvolta aplicaţii distribuite în mod transparent pentru programator, astfel încât acesta să apeleze metodele în acelaşi fel în care ar face-o pentru aplicaţii nedistribuite (folosind o sintaxă şi o structură semantică asemănătoare), încât folosirea de obiecte distribuite să fie similară cu utilizarea de obiecte locale, diferenţele fiind sintetizate mai jos:

Concept Obiect local Obiect “la distanţă”
definirea obiectului se face într-o clasă Java definirea comportamentului (exportat) al obiectului “la distanţă” se face printr-o interfaţă care trebuie să extindă interfaţa java.rmi.Remote
implementarea obiectului se face în clasa Java corespunzătoare comportamentul obiectului accesat “la distanţă” este realizat printr-o clasă care implementează interfaţa
crearea obiectului instanţa unui obiect nou se face prin operatorul new o instanţă nouă pentru un obiect aflat “la distanţă” se face folosind operatorul new pe maşina gazdă (clientul nu poate crea direct obiectul “la distanţă”)
accesul obiectului se realizează direct printr-o variabilă de tip referinţă un obiect “la distanţă” este accesat printr-o referinţă care indică spre un delegat al implementării interfeţei
referinţe o referinţă indică direct spre obiectul din heap corespunzător o referinţă “la distanţă” indică spre un obiect delegat (stub) alocat local în heap; obiectul delegat conţine informaţii ce permit conectarea la obiectul “la distanţă” unde este realizată implementarea metodelor
referinţe active un obiect este considerat “activ” dacă există cel puţin o referinţă către el într-un mediu distribuit unde maşinile virtuale pot manifesta erori critice sau conexiunea poate fi pierdută, o referinţă este activă pentru un obiect la distanţă dacă accesarea se face într-o anumită perioadă de timp (de închiriere); dacă pentru un obiect s-a renunţat (în mod explicit) la toate referinţele sau toate referinţele au expirat, obiectul “la distanţă” este disponibil pentru colectarea memoriei (eng. garbage collection) distribuită
finalizarea metoda finalize() (în caz că este definită) se apelează înainte ca memoria aferentă obiectului să fie dezalocată (prin garbage collector) dacă obiectul “la distanţă” implementează interfaţa Unreferrenced, metoda definită de această interfaţă este apelată când referinţele la obiect sunt distruse
colectarea memoriei disponibile un obiect spre care nu mai există referinţe (locale) devine candidat pentru mecanismul de colectare a memoriei disponibile (pentru dezalocarea memoriei aferente) există modul de colectare a memoriei distribuit care lucrează cu modulul garbage collector local, apelându-se atunci când nu există referinţe realizate “la distanţă” şi toate referinţele locale pentru obiectul accesat “la distanţă” au fost distruse
excepţii un program trebuie să trateze toate excepţiile (runtime sau alt tip) pentru asigurarea robusteţii aplicaţiilor distribuite, programele trebuie să trateze excepţiile RemoteException care ar putea fi generate

Arhitectura mecanismului RMI

Proiectarea mecanismului RMI a vizat implementarea unui model de obiecte distribuite în Java care să se integreze în limbajul de programare şi totodată să fie compatibil cu modelul de obiecte locale.

Aplicaţiile RMI sunt compuse, de cele mai multe ori, din două programe separate, server şi client.

Un server creează nişte obiecte la distanţă, face accesibile referinţele spre obiecte şi aşteaptă clienţii să invoce metodele pe care le-au definit.

Un client obţine o referinţă spre unul sau mai multe astfel de obiecte la distanţă care se găsesc pe server şi apoi invocă metodele pe ele.

RMI oferă un mecanism prin care serverul şi clientul comunică şi transferă informaţia în ambele sensuri. O astfel de aplicaţie este denumită cel mai frecvent aplicaţie (cu obiecte) distribuite.

Aplicaţiile distribuite trebuie să realizeze următoarele operaţii: localizarea obiectelor la distanţă, comunicarea cu obiectele la distanţă, încărcarea definiţiilor de clase pentru obiectele care sunt obţinute.

  • localizarea obiectelor la distanţă se poate face prin înregistrarea obiectelor “la distanţă” la serviciul de nume RMI (rmiregistry);
Alternativ, o aplicaţie poate transfera obiecte “la distanţă” prin intermediul unor invocări ale unor metode.
  • comunicarea cu obiectele la distanţă este realizată transparent de mecanismul RMI; pentru programator, comunicarea la distanţă se face similar cu invocarea metodelor locale;
  • încărcarea definiţiilor de clase pentru obiectele care sunt obţinute este posibilă concomitent cu transmiterea datelor obiectelor, având în vedere faptul că obiectele pot fi comunicate bidirecţional între client şi server.

Se observă că serverul foloseşte serviciul de nume (rmiregistry) pentru a înregistra un obiect pe care apoi clientul îl găseşte invocând acelaşi serviciu. Ulterior, clientul apelează o metodă a obiectului “la distanţă”. Definiţiile de clase sunt obţinute pentru obiect de pe un server web (existent), în ambele direcţii (de la server spre client şi de la client spre server).

Unul dintre avantajele principale puse la dispoziţie de mecanismul RMI este obţinerea definiţiei clasei unui obiect care nu este definit în maşina virtuală Java a clientului. Toate atributele şi metodele unui obiect, disponibile până acum într-o singură maşină virtuală, pot fi transmise către o alta, posibil la distanţă. Transmiterea obiectului se face prin clasa sa, astfel încât comportamentul nu se modifică atunci când acesta este transmis către altă maşină virtuală. Astfel, noi atribute şi comportamente sunt introduse într-o maşină virtuală aflată la distanţă, îmbogăţind astfel comportamentul aplicaţiei.

Ca orice aplicaţie Java, o aplicaţie distribuită folosind mecanismul RMI conţine interfeţe (pentru a declara metode) şi clase (pentru a implementa metodele definite în interfeţe şi, posibil, pentru a implementa metode noi). Unele implementări se pot găsi doar pe unele maşini virtuale. Obiectele invocate între maşini virtuale diferite sunt denumite obiecte “la distanţă” (eng. remote objects).

Un obiect “la distanţă” implementează o interfaţă (vizibilă “la distanţă”) având următoarele caracteristici:

Obiectele “la distanţă” sunt tratate diferit (faţă de obiectele locale) prin mecanismul RMI atunci când sunt transmise de la o maşină virtuală la alta.

În loc să se facă o copiere a implementării obiectului în maşina virtuală (client), mecanismul RMI transmite pentru obiectul la distanţă un ciot (eng. stub) care are rolul de delegat (eng. proxy), reprezentant local al obiectului şi reprezintă referinţa pentru acesta pe maşina virtuală (client). Astfel, se va invoca metoda pe obiectul ciot local ce este responsabil pentru execuţia metodei invocate în obiectul la distanţă.

Un ciot pentru un obiect la distanţă implementează acelaşi set de interfeţe pe care le implementează şi obiectul la distanţă, proprietate ce permite ciotului să poată fi convertit la oricare din interfeţele obiectului la distanţă. Totuşi, doar metodele definite în interfaţă sunt disponibile spre a fi invocate de client.

Noțiunea de serializare

Tehnologia RMI foloseşte mecanismul de serializare a obiectelor din Java pentru a transmite obiecte prin valoare între maşini virtuale diferite. Pentru ca un obiect să fie considerat serializabil, clasa sa trebuie să implementeze interfaţa java.io.Serializable.

Există două tipuri de clase care pot fi folosite cu mecanismul RMI:

  1. clase Remote ale căror instanţe pot fi folosite la distanţă, obiectele instanţă ale acestei clase putând fi accesate în două moduri:
    1. în spaţiul de adrese în care a fost creat, obiectul fiind utilizat ca orice alt obiect (local);
    2. în alte spaţii de adrese, obiectul va fi utilizat prin intermediul unui delegat, care impune unele limitări în accesarea sa comparativ cu metoda anterioară, dar care în linii generale permite folosirea obiectului ca şi acum ar fi accesat local;
  2. clase Serializable ale căror instanţe pot fi copiate între spaţii de adrese.

Dacă un obiect serializabil este dat ca parametru (sau valoare întoarsă) pentru un apel de metodă la distanţă, valoarea obiectului va fi copiată dintr-un spaţiu de adrese în altul. De asemenea, dacă un obiect la distanţă este transmis ca parametru (sau valoare întoarsă), delegatul acestuia va fi copiat dintr-un spaţiu de adrese în altul.

Majoritatea claselor standard sunt serializabile, deci o subclasă ale oricărora dintre acestea este de asemenea serializabilă. Orice informaţie dintr-o clasă serializabilă ar trebui să fie serializabilă.

Folosirea unui obiect serializabil în apelul unei metode la distanţă este foarte simplă. Se specifică obiectul dat ca parametru sau valoare întoarsă, tipul acestuia trebuind să fie o clasă serializabilă, fiind necesar ca atât clientul cât şi serverul să aibă acces la definiţia clasei serializabile care este utilizat, descărcarea definiţiilor claselor serializabile de pe o maşină virtuală pe alta trebuind să fie specificată prin politici de securitate.

Etapele dezvoltării unei aplicații distribuite folosind mecanismul RMI

Etapele dezvoltării unei aplicaţii distribuite folosind mecanismul RMI constau în:

  1. proiectarea şi implementarea componentelor aplicaţiei distribuite;
    Proiectarea presupune stabilirea componentelor locale şi ale componentelor accesibile la distanţă.
    1. definirea interfeţelor la distanţă, prin specificarea metodelor care pot fi apelate de client (nu şi a implementării lor !); trebuie precizate tipurile obiectelor care vor fi folosite ca parametri şi valori întoarse pentru metodele folosite; dacă nu se folosesc tipuri de bază ci tipuri ale unor obiecte definite de utilizator, acestea trebuie specificate de asemenea în acest moment;
    2. implementarea obiectelor “la distanţă”; obiectele la distanţă implementează una sau mai multe intefeţe, unele fiind accesibile doar local; trebuie implementate şi clasele (locale) care sunt folosite ca parametri sau valori întoarse pentru oricare dintre aceste metode;
    3. implementarea clientului poate fi realizată oricând după definirea interfeţelor la distanţă.
  2. compilarea surselor se face apelând compilatorul javac atât pentru interfeţele la distanţă, implementarea lor, alte clase pe server, cât şi pentru clasele client;
  3. “publicarea” claselor pentru a fi accesibile prin reţea (este vorba despre interfeţele la distanţă şi tipurile asociate precum şi definiţiile claselor care trebuie descărcate pe client sau pe server), de regulă, prin intermediul unui server web;
  4. pornirea aplicaţiei în ordinea: serviciul de nume RMI (rmiregistry), serverul şi clientul.

Aplicaţiile distribuite bazate pe mecanismul RMI sunt de regulă aplicaţii bazate pe comportamente căci sarcinile sunt încărcate dinamic de catre client fără a exista cunoştinţe în prealabil despre clasele care implementează sarcinile respective.

Exemplu

În cele ce urmează, implementarea unei aplicaţii distribuite folosind mecanismul RMI va fi ilustrată pe cazul particular al unei aplicații integrate pentru întreprinderi utilizată de o librărie virtuală.

Aceasta oferă mai multe servicii, dedicate unor categorii diferite de utilizatori. Astfel, pot fi accesate informații cu privire la volumele comercializate, date privind formatele de prezentare ale acestora precum și aspecte care îi privesc pe scriitorii care au elaborat lucrările respective.

Aplicaţia va avea arhitectura client-server şi implementează un model de comunicare strâns cuplat, în care apelurile metodelor este realizat în mod sincron.

Pe server se va realiza accesul la informațiile stocate în baza de date precum și prelucrarea acestora în funcție de necesități.

Un client poate accesa mai multe funcționalități, aparținând unor servicii diferite:

  • serviciul de carte (BookManager) oferă informații cu privire la volumele comercializate; astfel, poate fi obținută lista tuturor lucrărilor care pot fi achiziționate din librărie, aceasta putând fi ulterior filtrată în funcție de stocul disponibil, datele accesate în această situație fiind mult mai detaliate:
  • serviciul referitor la formatele de prezentare ale volumelor (BookPresentationManager) permite actualizarea prețurilor pentru o categorie de lucrări care se găsesc într-un anumit număr de formate de prezentare, oferind totodată posibilitatea de a realiza comenzi de aprovizionare pentru acele volume pentru care nu este întrunit un anumit stoc;
  • serviciul de furnizare a informațiilor cu privire la scriitori (WriterManager) permite înlăturarea din baza de date a acelor autori ale căror lucrări nu sunt comercializate de librărie dar și filtrarea listei de înregistrări în funcție de numărul total de cărți publicate, numărul de cărți pe care le-au scris singuri sau numărul de cărți redactate în colaborare.

Clase și Interfețe "la distanță"

Protocolul care permite ca serverul să fie interogat cu cereri de către client care să fie rulate apoi de server iar rezultatul să fie întors la client este realizat prin intermediul unor interfeţe care descriu funcţionalitatea ce poate fi accesată la distanţă.

*ManagerInterface.java
package ro.pub.cs.aipi.lab03.businesslogic;
 
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.List;
 
import ro.pub.cs.aipi.lab03.result.*Information;
 
public interface *ManagerInterface extends Remote {
  public List<*Information> method1(int param) throws RemoteException;
  public List<*Information> method2(String param) throws RemoteException;
}
Prin noteția * se înțelege faptul că acest caracter poate fi înlocuit cu orice unitate de gestiune a unui serviciu, respectiv cu orice entitate persistentă.

Prin extinderea interfeţei java.rmi.Remote se specifică faptul că metodele declarate de interfaţa Reservation vor putea fi apelate din altă maşină virtuală Java. Orice obiect care implementează interfaţa Reservation va putea fi considerat un obiect “la distanţă”.

Metodele definite în cadrul acestei interfeţe vor putea fi apelate din cadrul altei maşini virtuale (“la distanţă”), prin urmare este obligatoriu să genereze excepţii de tip java.rmi.RemoteException. O astfel de excepţie este aruncată de sistemul RMI dintr-un apel de metodă la distanţă pentru indicarea situaţiei în care a intervenit o eroare de comunicare sau de protocol. Codul sursă care apelează metode “la distanţă” trebuie să trateze o astfel de excepţie (prin prinderea ei sau transmiterea ei mai departe).

Se observă că metodele method1() și method2() au parametri de tip date primitive, întoarcând un rezultat de tip *Information, derivate din PersistentEntity. Întrucât acestea nu reprezintă un tip de bază, ele trebuie să fie serializabile. Se pot defini ca serializabile tipurile de date *Information, fiind de asemenea posibil ca această proprietate să aparțină totodată clasei din care acestea sunt extinse. Proprietățile acestor clase nu trebuie să poată fi accesate decât prin intermediul unor metode de tip getter / setter.

*Information.java
public class *Information extends PersistentEntity implements Serializable {
 
  private static final long serialVersionUID = 20152015L;
 
  // ..
}

sau

PersistentEntity.java
@Embeddable
public class PersistentEntity implements Serializable {
  // ..
}

Câmpul serialVersionUID e folosit în procesul de deserializare al obiectelor încât dacă atributul obţinut nu corespunde cu cel specificat în definiţia clasei, este aruncată o excepţie de tipul InvalidClassException.

Obiectele de tip *Information vor fi transmise între client şi server astfel încât ele trebuie să fie serializabile (asigurând astfel consistenţa informaţiilor reţinute în obiect indiferent de formatul de reprezentare din maşinile virtuale). De aceea, toate clasele *Information implementează interfaţa Serializable (din pachetul java.io) punând la dispoziţie un constructor de copiere şi metode de tip setter/getter .

Compilarea claselor care vor fi incluse în bibliotecile care vor fi expuse la distanță se face în mod obişnuit:

  • Linux
    student@aipi2015:~$ javac -cp ../libs/hibernate-jpa-2.1-api-1.0.0.Final.jar ro/pub/cs/aipi/lab03/businesslogic/*Interface.java ro/pub/cs/aipi/lab03/general/Constants.java ro/pub/cs/aipi/lab03/entities/PersistentEntity.java ro/pub/cs/aipi/lab03/result/*.java
  • Windows
    C:\Users\Aipi2015> javac -cp ..\\libs\\hibernate-jpa-2.1-api-1.0.0.Final.jar ro\\pub\\cs\\aipi\\lab03\\businesslogic\\*Interface.java ro\\pub\\cs\\aipi\\lab03\\general\\Constants.java ro\\pub\\cs\\aipi\\lab03\\entities\\PersistentEntity.java ro\\pub\\cs\\aipi\\lab03\\result\\*.java

Clasele reprezentând interfețe care conțin metode la distanță, tipuri de date definite de utilizator, implicate în transferul de parametri sau valori întoarse în cazul metodelor la distanță precum și alte clase referite de acestea, sunt necesare atât pe client cât si pe server, fiind necesar să fie împachetate într-o arhivă Java (de tip .jar) adăugată în classpath atât la compilare cât şi la rulare. Aceasta se constituie în definiția de clase care va trebui să fie cunoscută de serviciul de nume pentru a putea fi încărcată atunci când este necesar să se acceseze funcționalitatea disponibilă la distanță.

Se recomandă ca în cazul unor funcționalități diferite, accesate de diverși utilizatori, să se creeze definiții de clase separate, care vor fi expuse către serviciul de nume în mod distinct, astfel încât să poată fi accesate numai în funcție de drepturile stabilite în contextul aplicației integrate pentru întrerprinderi. În situația în care unele clase sunt accesate din arhive .jar diferite, se va construi și o arhivă cu resursele comune, aceasta putând fi partajată între toate entitățile implicate.
  • Linux
    student@aipi2015:~$ jar cvf bookstore-common.jar ro/pub/cs/aipi/lab03/general/Constants.class ro/pub/cs/aipi/lab03/entities/PersistentEntity.class
    student@aipi2015:~$ jar cvf bookstore-bookmanager.jar ro/pub/cs/aipi/lab03/businesslogic/BookManagerInterface.class ro/pub/cs/aipi/lab03/result/BookInformation.class ro/pub/cs/aipi/lab03/result/BookInformationDetailed.class
    student@aipi2015:~$ jar cvf bookstore-bookpresentationmanager.jar ro/pub/cs/aipi/lab03/businesslogic/BookPresentationManagerInterface.class ro/pub/cs/aipi/lab03/result/BookPresentationInformation.class ro/pub/cs/aipi/lab03/result/SupplyOrderInformation.class
    student@aipi2015:~$ jar cvf bookstore-writermanager.jar ro/pub/cs/aipi/lab03/businesslogic/WriterManagerInterface.class ro/pub/cs/aipi/lab03/result/WriterInformation.class
  • Windows
    C:\Users\Aipi2015> jar cvf bookstore-common.jar ro\\pub\\cs\\aipi\\lab03\\general\\Constants.class ro\\pub\\cs\\aipi\\lab03\\entities\\PersistentEntity.class
    C:\Users\Aipi2015> jar cvf bookstore-bookmanager.jar ro\\pub\\cs\\aipi\\lab03\\businesslogic\\BookManagerInterface.class ro\\pub\\cs\\aipi\\lab03\\result\\BookInformation.class ro\\pub\\cs\\aipi\\lab03\\result\\BookInformationDetailed.class
    C:\Users\Aipi2015> jar cvf bookstore-bookpresentationmanager.jar ro\\pub\\cs\\aipi\\lab03\\businesslogic\\BookPresentationManagerInterface.class ro\\pub\\cs\\aipi\\lab03\\result\\BookPresentationInformation.class ro\\pub\\cs\\aipi\\lab03\\result\\SupplyOrderInformation.class
    C:\Users\Aipi2015> jar cvf bookstore-writermanager.jar ro\\pub\\cs\\aipi\\lab03\\businesslogic\\WriterManagerInterface.class ro\\pub\\cs\\aipi\\lab03\\result\\WriterInformation.class

Astfel, au fost create arhive .jar conținând definiții de clase corespunzătoare diferitelor funcționalități (bookstore-bookmanager.jar, bookstore-bookpresentationmanager.jar, bookstore-writermanager.jar) precum și o bibliotecă comună pe care o partajează (bookstore-common.jar - conținând informații generale despre diferite constante din sistemul informatic precum și clasa de baza PersistentEntity).

Dezvoltarea serverului RMI

Serverul trebuie să implementeze interfaţa “la distanţă” vizibilă în reţea astfel încât comportamentul obiectelor care vor fi accesate de client să fie definit (obiectele “la distanţă” sunt obţinute prin instanţierea clasei Server). Pe server pot fi definite şi alte metode (în afara celei impuse de interfaţă) care vor putea fi accesate local, între care constructorul clasei şi metoda main().

Argumentele sau valorile întoarse din metodele “la distanţă” (definite în interfaţă) pot fi de orice tip, inclusiv obiecte locale, obiecte “la distanţă” sau chiar tipuri primitive de date. Astfel, orice entitate având orice tip poate fi transmisă către sau de la o metodă “la distanţă” cât timp aceasta este o instanţă a unui tip care este fie primitiv, fie obiect “la distanţă” sau obiect local serializabil (aşa cum s-a precizat şi mai sus). Alte tipuri de obiecte, cum ar fi fire de execuţie sau descriptori de fişier, având sens doar în spaţiul de adrese în care rulează, nu pot să fie utilizaţi ca argumente sau valori întoarse în metode “la distanţă”. Totuşi, majoritatea claselor din pachetele java.lang şi java.util implementează interfaţa java.io.Serializable.

Obiectele “la distanţă” sunt transmise prin referinţă (un ciot care este delegatul la nivelul clientului şi care implementează setul complet de interfeţe accesibile prin reţea specificate de obiect).

Sunt vizibile doar metodele definite în interfaţa “la distanţă” pentru obiectul respectiv. Metodele definite pentru obiect care nu sunt definite în interfaţă nu vor putea fi accesate la distanţă.

Orice modificare realizată asupra stării obiectului prin apelurile la distanţă sunt reflectate în obiectul original (de pe server).

Obiectele locale sunt transmise prin valoare (este realizată o copie folosind mecanismul de serializare). Sunt copiate toate câmpurile care nu au precizat atributul static sau transient. Orice modificare realizată în starea obiectului sunt reflectate doar în copie, dar nu şi în instanţa originală. Similar, schimbările asupra stării obiectului în instanţa originală nu vor fi reflectate în copie.

BookStore.java
package ro.pub.cs.aipi.lab03.main;
 
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
 
import ro.pub.cs.aipi.lab03.businesslogic.*Manager;
import ro.pub.cs.aipi.lab03.businesslogic.*ManagerInterface;
import ro.pub.cs.aipi.lab03.general.Constants;
 
public class BookStore {
 
  public static void main(String[] args) {
    if (System.getSecurityManager() == null) {
      System.setSecurityManager(new SecurityManager());
    }
 
    try {
      Registry registry = LocateRegistry.getRegistry();
      String *ManagerServiceName = Constants.*_MANAGER_SERVICE_NAME;
      *ManagerInterface *ManagerService = new *Manager();
      *ManagerInterface *ManagerProxy = (*ManagerInterface)UnicastRemoteObject.exportObject(*ManagerService, 0);
      registry.rebind(*ManagerServiceName, *ManagerProxy);
      System.out.format("[%s] successfully bounded%n", Constants.*_MANAGER_SERVICE_NAME);
    } catch (Exception exception) {
    System.out.println("An exception has occurred: " + exception.getMessage());
    if (Constants.DEBUG) {
      exception.printStackTrace();
    }
  }
}

Metoda main() este cea mai complexă, fiind folosită pentru a porni serverul, realizând totodată şi iniţializările necesare pentru a pregăti serverul să accepte conexiuni dinspre clienţi. Metoda nu poate fi apelată din altă maşină virtuală, pentru că nu extinde interfaţa Remote. Fiind statică, ea este asociată cu clasa şi nu cu obiectul.

  1. Este creat şi instalat un sistem de securitate care protejează accesul la resursele sistemului de cod sursă (descărcat) care rulează în maşina virtuală Java. Acest sistem de securitate stabileşte dacă codul sursă poate să acceseze sistemul local de fişiere sau poate realiza alte operaţii privilegiate.

    În situaţia în care aplicaţia distribuită care foloseşte tehnologia RMI nu dispune de un sistem de securitate, nu vor fi descărcate clase (altele decât din sistemul de fişiere specificat în classpath) pentru obiectele primite ca argumente sau valori întoarse.

    Se asigură totodată faptul că operaţiile realizate de codul descărcat sunt conforme cu o politică de securitate.
  2. Se creează instanţe ale claselor *Manager care sunt exportate către serviciul de nume RMI folosindu-se metoda UnicastRemoteObject.exportObject() astfel încât obiectele să poată fi apelate de către clienţi de la distanţă. La întoarcerea din execuţia acestei metode, obiectul poate procesa apeluri “la distanţă”. Cel de-al doilea argument al metodei exportObject() reprezintă un port TCP utilizat pentru ascultarea apelurilor metodelor la distanţă de către client. Valoarea 0, folosită în exemplul de faţă specifică utilizarea unui port anonim, ales de RMI sau de sistemul de operare.

    Este întors un ciot (eng. stub) ce are tipul *ManagerInterface, deci tipul interfeţei şi nu al clasei (pentru că ciotul implementează doar interfaţa “la distanţă”).
  3. Înainte ca un client să poată invoca metodele unui obiect “la distanţă”, trebuie să obţină referinţa spre acesta, operaţie ce poate fi realizată în acelaşi fel în care este obţinută o referinţă spre un obiect într-o aplicaţie (ca valoare întoarsă pentru o metodă sau ca un câmp al unei structuri de date care conţine referinţa).

Mecanismul RMI pune la dispoziţie un tip particular de obiect “la distanţă” și anume serviciul de nume (RMI registry), pentru găsirea de referinţe către alte obiecte de acest tip prin specificarea unui nume. Un astfel de sistem este în mod obişnuit folosit doar pentru găsirea primului obiect solicitat de client, putând ajuta la găsirea altor obiecte.

Interfaţa java.rmi.registry.Registry conţine specificaţiile pentru înregistrarea şi găsirea de obiecte “la distanţă” în serviciul de nume. Clasa java.rmi.registry.LocateRegistry pune la dispoziţie metode statice pentru identificarea unei referinţe la distanţă într-o reţea anume (specificată prin adresă şi port). Metodele creează referinţa către obiectul “la distanţă” conţinând adresa de reţea specificată fără a se realiza comunicare la distanţă. De asemenea, clasa java.rmi.registry.LocateRegistry pune la dispoziţie metode statice pentru crearea unui serviciu de nume nou în maşina virtuală Java.

După ce obiectul “la distanţă” a fost înregistrat de sistemul de nume, clienţii de pe orice maşină îl pot căuta după nume, îi pot obţine referinţa şi pot apela metodele “la distanţă” de pe el. Serverele de pe o maşină pot partaja acelaşi serviciu de nume sau fiecare aplicaţie (de tip server) poate să îşi creeze şi să utilizeze propriul său sistem de nume.

Metoda rebind() reprezintă un apel la distanţă către serviciul de nume RMI şi poate avea ca rezultat aruncarea unei excepţii de tip RemoteException.

Metoda LocateRegistry.getRegistry() este apelată fără parametri, obţinându-se o referinţă la serviciul de nume de pe maşina locală şi portul 1099, definit implicit. Dacă serviciul de nume a fost creat pe un alt port, se foloseşte o metodă supraîncărcată care primeşte un parametru de tip int care specifică portul.

Când se face apelul la distanţă către serviciul de nume, se obţine un obiect de tip ciot (în loc de copia obiectului) pentru că obiectele care implementează interfeţe de tip Remote rămân în cadrul maşinii virtuale Java în care au fost create. Astfel, atunci când un client realizează o căutare în serviciul de nume al serverului, se întoarce o copie a obiectului de tip ciot, obiectele “la distanţă” fiind transmise deci prin referinţă.

Din motive de securitate, o aplicaţie poate (re)construi sau distruge legătura dintre o referinţă a unui obiect la distanţă şi serviciul de nume care rulează pe aceeaşi maşină, prevenind situaţia în care o altă maşină ar înlătura sau suprascrie intrările într-un serviciu de nume.

Operaţia de căutare (lookup()) poate fi realizată de pe orice maşină, locală sau “la distanţă”.

Excepţiile ce pot fi aruncate de metoda main() sunt de tip RemoteException, generate fie de metoda UnicastRemoteObject.exportObject() sau de apelul rebind(). Se poate realiza recuperarea din eroare (în caz că o excepţie a fost aruncată) prin reapelarea metodei care a generat-o sau prin folosirea unui alt server.

După realizarea acestor operaţii, metoda main() se încheie. Nu este necesară definirea unui fir de execuţie care să ţină serverul în starea de rulare, pentru că atât timp cât există o referinţă către un obiect de tip *Manager într-o maşină virtuală (locală sau la distanţă), el nu va fi distrus de garbage collector. Mecanismul RMI menţine activ procesul asociat obiectelor *Manager, pentru că există o legătură către acesta în serviciul de nume, accesibilă la distanţă. Obiectul va fi distrus atunci când legătura dintre el şi serviciul de nume va fi eliberată şi nu vor mai exista clienţi la distanţă care să aibă referinţe către el.

Lansarea în execuţie nu poate fi realizată decât după:

  • pornirea serviciului de nume, care permite clienţilor să obţină o referinţă către un obiect la distanţă
Parametrul port este opţional, el poate să nu fie menţionat, caz în care serviciul de nume foloseşte portul implicit 1099.
  • Unix
    student@aipi2015:~$ rmiregistry [port] -J-Djava.rmi.server.useCodebaseOnly=false &
  • Windows
    C:\Users\Aipi2015> start rmiregistry [port] -J-Djava.rmi.server.useCodebaseOnly=false
  • specificarea unei politici de securitate, într-un fişier denumit policy[.txt], codul sursă obţinând permisiunile de care are nevoie pentru a putea rula:
    grant codebase "file://<absolute_path>/03-BookStore-RMI-Server/-" {
      permission java.security.AllPermission;
    };

Toate permisiunile sunt acordate claselor din calea locală a aplicaţiei pentru că acesta are un grad de încredere ridicat, dar nu se acordă drepturi pentru codul sursă descărcat din alte maşini virtuale.

Lansarea în execuţie a serverului se face prin comanda:

  • Linux
    student@aipi2015:~$ java -classpath .:libs/bookstore-common.jar:libs/bookstore-*manager.jar:... -Djava.rmi.server.codebase="file:///<absolute_path>/03-BookStore-RMI-Server/libs/bookstore-common.jar file:///<absolute_path>/03-BookStore-RMI-Server/libs/bookstore-*manager.jar ..." -Djava.rmi.server.hostname=localhost -Djava.security.policy=configuration/policy ro.pub.cs.aipi.lab03.main.BookStore
  • Windows
    C:\Users\Aipi2015> java -classpath .;libs/bookstore-common.jar;libs/bookstore-*manager.jar;... -Djava.rmi.server.codebase="file:///<absolute_path>/03-BookStore-RMI-Server/libs/bookstore-common.jar file:///<absolute_path>/03-BookStore-RMI-Server/libs/bookstore-*manager.jar ..." -Djava.rmi.server.hostname=localhost -Djava.security.policy=configuration/policy ro.pub.cs.aipi.lab03.main.BookStore
Prin notația … se înțelege faptul că în continuare pot urma și alte elemente de același tip.

Proprietatea java.rmi.server.codebase specifică locaţia de unde pot fi încărcate definiţii pentru clasele provenind de la server. Dacă în locul unei arhive se specifică o ierarhie de directoare, aceasta trebuie încheiată prin caracterul ’/’. În situația în care sunt incluse mai multe fișiere, acestea trebuie să fie delimitate prin separatorul spațiu. Pentru fiecare resursă trebuie indicat și protocoul prin care aceasta este accesată (file:/// sau http://).

Proprietatea java.rmi.server.hostname specifică numele maşinii sau adresa care urmează a fi completată în obiectele de tip ciot care vor fi exportate. Aceeaşi valoare va fi folosită de clienţi când vor încerca să apeleze metodele la distanţă. Implicit, implementarea RMI foloseşte adresa IP indicată de java.net.InetAddress.getLocalHost(). Totuşi, câteodată o astfel de adresă nu este întotdeauna potrivită pentru toţi clienţii şi un nume (al maşinii) este mai adecvat. Pentru ca numele sa fie vizibil de toţi clienţii, trebuie configurată proprietatea java.rmi.server.hostname.

Proprietatea java.rmi.policy specifică politica de securitatecare conţine permisiunile ce vor fi acordate.

Dezvoltarea clientului RMI

Codul care apelează metodele unui obiect tip *Manager trebuie să obţină o referinţă către acesta.

  1. Ca şi în cazul serverului, iniţial se va instala un sistem de securitate, necesar pentru că în procesul de obţinere a referinţei către obiectul ciot corespunzător obiectului la distanţă se poate întâmpla să se descarce definiţii de clase de la server, lucru care este posibil doar în condiţiile existenţei unui sistem de securitate.
  2. Clientul va căuta obiectul “la distanţă” specificând un nume (acelaşi folosit de server pentru a înregistra obiectul la serviciul de nume) – în acest sens, e folosită de asemenea metoda LocateRegistry.getRegistry() pentru a obţine referinţa la sistemul de nume RMI care rulează pe maşina server, parametrul cu care este apelat reprezentând numele maşinii pe care rulează obiectul de tip *Manager. În mod implicit, se consideră că sistemul de nume RMI ascultă pe portul 1099. În plus, poate fi folosită o metodă supraîncărcată care să specifice şi portul, prin specificarea unui parametru de tip int. Apoi este utilizată metoda lookup() pentru a căuta obiectul la distanţă prin nume în sistemul de nume de pe maşina server.
  3. Se vor apela metodele la distanţă, afişându-se rezultatul.
*ManagerClient.java
package ro.pub.cs.aipi.lab03.main;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
 
import ro.pub.cs.aipi.lab03.businesslogic.*ManagerInterface;
import ro.pub.cs.aipi.lab03.general.Constants;
import ro.pub.cs.aipi.lab03.general.Utilities;
import ro.pub.cs.aipi.lab03.result.*Information;
 
public class *ManagerClient {
 
  public static void main(String[] args) {
    if (args.length != 1) {
      System.out.println("You must specify the IP address on which the rmiregistry runs!");
      return;
    }
 
    if (System.getSecurityManager() == null) {
      System.setSecurityManager(new SecurityManager());
    }
 
    try {
      String serviceName = Constants.SERVICE_NAME;
      Registry registry = LocateRegistry.getRegistry(args[0]);
      *ManagerInterface service = (*ManagerInterface) registry.lookup(serviceName);
      int option = -1;
      do {
        Utilities.displayMenu();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        option = Integer.parseInt(bufferedReader.readLine());
        switch (option) {
          case Constants.OPTION_1:
            Utilities.displayResults(service.method1(), *Information.class);
            break;
          case Constants.OPTION_2:
            Utilities.displayResults(service.method2(), *Information.class);
            break;
          // ...
          case Constants.OPTION_QUIT:
            break;
          default:
            System.out.println("You have entered an invalid option");
            break;
        }
      } while (option != Constants.OPTION_QUIT);
    } catch (Exception exception) {
      System.out.println("An exception has occurred: " + exception.getMessage());
      if (Constants.DEBUG) {
        exception.printStackTrace();
      }
    }
  }
}

Lansarea în execuţie nu poate fi realizată decât după:

  • pornirea serviciului de nume RMI pe maşina server;
  • lansarea în execuţie a aplicaţiei server BookStore;
  • specificarea unei politici de securitate, într-un fişier numit policy[.txt], codul sursă obţinând permisiunile de care are nevoie pentru a putea rula:
    grant codebase "file://<absolute_path>/03-BookStore-RMI-Client-*Manager/-" {
      permission java.security.AllPermission;
    };

Lansarea în execuţie a clientului se face prin comanda:

  • Linux
    student@aipi2015:~$ java -classpath .:libs/bookstore-common.jar:libs/bookstore-*manager.jar -Djava.rmi.server.codebase="file:///<absolute_path>/03-BookStore-RMI-Client-*Manager/libs/bookstore-common.jar file:///<absolute_path>/03-BookStore-RMI-Client-*Manager/libs/bookstore-*manager.jar" -Djava.security.policy=configuration/policy ro.pub.cs.aipi.lab03.main.*ManagerClient
  • Windows
    C:\Users\Aipi2015> java -classpath .;libs/bookstore-common.jar;libs/bookstore-*manager.jar -Djava.rmi.server.codebase="file:///<absolute_path>/03-BookStore-RMI-Client-*Manager/libs/bookstore-common.jar file:///<absolute_path>/03-BookStore-RMI-Client-*Manager/libs/bookstore-*manager.jar" -Djava.rmi.server.hostname=localhost -Djava.security.policy=configuration/policy ro.pub.cs.aipi.lab03.main.*ManagerClient

Se observă că prin proprietatea java.rmi.server.codebase s-a precizat locaţia unde clientul îşi încarcă definițiile de clase, iar prin proprietatea java.security.policy s-a specificat fişierul care conţine permisiunile acordate pentru diferite coduri sursă.

Configurarea mediilor de dezvoltare integrate pentru specificarea parametrilor de rulare

Configuraţiile de rulare implică specificarea proprietăţilor Java java.rmi.server.codebase (ce indică spre arhivele .jar ce conţin definițiile de clase care trebuie încărcate de către server și de către client), java.rmi.server.hostname pentru server şi java.security.policy atât pentru server cât şi pentru client.

Proprietatea codebase din fişierul ce conţine politica de securitate (atât pentru server cât şi pentru client) trebuie să ofere permisiuni maxime (java.security.AllPermission) pentru toate clasele aflate în structura de directoare a serverului, respectiv a clientului. De altfel, aceasta este opțională.

Nu este neapărat necesar ca proprietățile să fie specificate în cadrul configurațiilor de rulare, acestea pot fi precizate și în codul sursă (înainte de a se implementa politica de securitate):
System.setProperty("java.rmi.server.codebase", "...");
System.setProperty("java.security.policy", "...");

Eclipse Mars (4.5)

Pentru a se defini o configurație de rulare, se accesează meniul contextual asociat pictogramei Run (prin intermediul săgeții asociate), selectându-se opțiunea Run Configurations….

Se definește o configurație de rulare de tip Java Application. Indicându-se această valoare, se accesează pictograma corespunzătoare acțiunii New launch configuration sau se folosește meniul contextual.

Server

Pentru server se definesc:

  • denumirea configurației de rulare;
  • proiectul;
  • denumirea clasei principale (calificată complet - prefixată de denumirea pachetului);

  • argumentele cu care se rulează programul (dacă există);
  • argumentele mașinii virtuale:
    • calea (-classpath);
    • proprietățile de sistem:
      • java.rmi.server.codebase;
      • java.rmi.server.hostname;
      • java.security.policy.

Client

Pentru client se definesc:

  • denumirea configurației de rulare;
  • proiectul;
  • denumirea clasei principale (calificată complet - prefixată de denumirea pachetului);

  • argumentele cu care se rulează programul (dacă există);
  • argumentele mașinii virtuale:
    • calea (-classpath);
    • proprietățile de sistem:
      • java.rmi.server.codebase;
      • java.security.policy.

NetBeans 8.0.2

Pentru a defini o configurație de rulare, se accesează lista de selecție conținând astfel de obiecte (în stânga pictogramei corespunzătoare acțiunii de rulare a proiectului) și se accesează opțiunea Customize….

În mediul integrat de dezvoltare NetBeans configurațiile de rulare sunt stocate în mod automat în preferințele proiectului, astfel încât acestea pot fi utilizate și atunci când proiectul este rulat pe mașini diferite.

Pentru fiecare proiect pot fi definite mai multe configurații de rulare, acestea fiind încărcate în mod automat în momentul în care se modifică selecția care indică proiectul curent. După ce o astfel de configurație de rulare este aleasă din lista de opțiuni, lansarea în execuție a proiectului poate fi realizată prin accesarea pictogramei aferente acestei acțiuni.

Server

Pentru server se definesc următoarele proprietăți:

  • denumirea configurației de rulare;
  • clasa principală a proiectului;
  • argumentele în linia de comandă;
  • argumentele cu care se rulează mașina virtuală:
    • calea (classpath) conține toate bibliotecile accesate de aplicație;
    • proprietăți de sistem:
      • java.rmi.server.codebase: indică locațiile la care se găsesc bibliotecile care conțin definițiile de clase care vor fi încărcate pentru a fi expuse la distanță;
      • java.rmi.server.hostname: precizează adresa IP la care este disponibil serverul;
      • java.security.policy: definește locația la care se găsește fișierul ce conține politica de securitate în privința accesului la codul sursă.

Client

Pentru server se definesc următoarele proprietăți:

  • denumirea configurației de rulare;
  • clasa principală a proiectului;
  • argumentele în linia de comandă;
  • argumentele cu care se rulează mașina virtuală:
    • calea (classpath) conține toate bibliotecile accesate de aplicație;
    • proprietăți de sistem:
      • java.rmi.server.codebase: indică locațiile la care se găsesc bibliotecile care conțin definițiile de clase, acestea fiind încărcate pentru a cunoaște funcționalitățile care pot fi accesate la distanță;
      • java.security.policy: definește locația la care se găsește fișierul ce conține politica de securitate în privința accesului la codul sursă.

Activitate de Laborator

0. Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la https://www.github.com/aipi2015/Laborator03. În urma acestei operații, directorul Laborator03 va trebui să conțină subdirectorul labtasks, fișierele README.md și LICENSE.

student@aipi2015:~$ git clone https://www.github.com/aipi2015/Laborator03.git
Modificați în fișierul persistence.xml din directorul src/META-INF informațiile necesare obținerii drepturilor de acces la baza de date (nume de utilizator, parola).

1. Să se ruleze, folosind MySQL Workbench (sau alt utilitar similar), scriptul Laborator03.sql, localizat în directorul src/META-INF/scripts. Acesta instalează baza de date bookstore.

În cazul în care baza de date bookstore este deja instalată, nu mai este necesar să se ruleze acest script.
Această operație poate dura o perioadă de timp îndelungată, în funcție de configurațiile mașinilor pe care se lucrează.

2. Să se pornească registrul de nume rmiregistry prin apelarea scriptului startRmiRegistry [.bat|.sh], localizate în directorul scripts.

  • Linux
    student@aipi2015:~$ ./startRmiRegistry.sh
În situația în care scriptul nu are drepturi de execuție, acestea vor fi acordate
student@aipi2015:~$ chmod +x startRmiRegistry.sh
  • Windows
    C:\Users\Aipi2015> startRmiRegistry.bat

Î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/local/java/jdk1.8.0_60/bin
  • Windows: Computer PropertiesControl Panel Home/Advanced System PropertiesSystem Properties/Advanced/Environment VariablesSystem Variables / Path / Edit
    SET PATH=%PATH%;C:\Program Files\Java\jdk1.8.0_60\bin





Verificaţi că procesul rmiregistry rulează înainte de a porni serverul.
  • Linux
    student@aipi2015:~$ ps -a
  • Windows - procesul apare în lista Task Manager
Dacă aveţi mai multe versiuni de Java instalate, verificaţi faptul că serviciul de nume rmiregistry corespunde versiunii cu care dezvoltaţi aplicaţiile, altfel conexiunea la serviciul de nume va eşua.

3. a) Să se recreeze arhivele .jar conținând definițiile de clase rulând scriptul makeJars [.bat|.sh], localizat în directorul scripts, care:

  • compilează doar codul sursă ce conține interfețele la distanță, clasele ale căror obiecte sunt transmise ca parametri / valori întoarse metodelor la distanță precum și alte clase referite de acestea;
  • împachetează în arhive .jar corespunzătoare fiecărei funcționalități fișierele astfel generate;
    • bookstore-common.jar - conține clasele comune tuturor definițiilor de clase; creearea acestei arhive este necesară pentru a evita conflictele ce pot apărea prin includerea tuturor în aceeași cale;
    • bookstore-bookmanager.jar - conține clasele ce oferă funcționalitatea precizată de interfața BookManagerInterface;
    • bookstore-bookpresentationmanager.jar - conține clasele ce oferă funcționalitatea precizată de interfața BookPresentationManagerInterface;
    • bookstore-writermanager.jar - conține clasele ce oferă funcționalitatea precizată de interfața WriterManagerInterface;
  • suprascrie bibliotecile referite de fiecare proiect în parte (în cale), în directorul lib/.
  • Linux
    student@aipi2015:~$ ./makeJars.sh
  • Windows
    C:\Users\Aipi2015> makeJars.bat
Se asigură astfel că sursele încărcate au fost compilate folosind aceeași versiune de Java cu care vor fi rulate serverul / clientul (și pe care o folosește și registrul de nume rmiregistry), care vor folosi definițiile de clase respective.

b) Să se modifice:

  • căile absolute din fişierul policy corespunzător serverului și clientului.

Acesta are, de regulă, forma:

policy
grant codebase "file:///<absolute_path>/03-BookStore-RMI-Server/-" {
  permission java.security.AllPermission;
};
policy
grant codebase "file:///<absolute_path>/03-BookStore-RMI-Client-BookManager/-" {
  permission java.security.AllPermission;
};

Totodată, se poate utiliza și forma simplificată (mai ales atunci când apar erori legate de permisiuni, fiind necesar să se identifice cauza acestora):

policy
grant {
  permission java.security.AllPermission;
};
  • configuraţiile de rulare (Eclipse / Netbeans) pentru rularea serverului și a clientului; este necesar să se definească următoarele proprietăți de sistem:
    • java.rmi.server.codebase - locațiile la care se găsesc arhivele .jar care conțin arhivele de clase; se folosesc numai locații absolute, indicându-se și protocolul prin care se accesează fișierul (http://, respectiv file:///); în cazul în care există mai multe definiții de clase, acestea sunt delimitate prin caracterul spațiu;
    • java.rmi.server.hostname (doar pentru server) - adresa IP a mașinii pe care rulează serverul;
    • java.security.policy - locația la care se găsește fișierul ce definește politica de securitate (pot fi folosite locații relative);

4. Să se ruleze, în ordine, proiectele corespunzătoare serverului, respectiv clientului, prin intermediul configurațiilor de rulare definite anterior. Să se testeze funcționalitățile disponibile, verificându-se conectivitatea dintre server și client.

5. Să se expună metodele corespunzătoare clasei BookPresentationManager, astfel încât acestea să fie accesibile la distanță:

a) Să se definească interfața BookPresentationManagerInterface în pachetul ro.pub.cs.aipi.lab03.businesslogic; aceasta trebuie să fie derivată din java.rmi.Remote, iar metodele sale, vizibile în contextul altor mașini virtuale, trebuie să poată genera excepția java.rmi.RemoteException;

b) Să se modifice clasa BookPresentationManager astfel încât să implementeze interfața accesibilă la distanță, schimbându-se și semnăturile metodelor pe care le conține;

c) Să se modifice clasele entitate BookPresentationInformation, respectiv SupplyOrderInformation din pachetul ro.pub.cs.aipi.lab03.result astfel încât obiectele acestora să poată fi transmise prin rețea, ca parametri sau ca valori întoarse ale obiectelor accesibile la distanță. În acest sens, acestea trebuie să implementeze interfața java.io.Serializable.

d) Să se expună, în clasa ro.pub.cs.aipi.lab03.bookstore.main.BookStore, funcționalitățile definite de interfața BookPresentationManagerInterface, ca serviciu accesibil prin intermediul rmiregistry, identificarea sa fiind realizată prin intermediul unei denumiri.

e) Să se modifice scriptul de creare makeJars [.sh|.bat] astfel încât:

  1. să construiască arhiva bookstore-bookpresentationmanager.jar care conține interfața BookPresentationManagerInterface și clasele entitate BookPresentationInformation, respectiv SupplyOrderInformation;
  2. să copieze aceste biblioteci în calea corespunzătoare proiectului care le va încărca pentru a le accesa funcționalitățile (după ce se creează un astfel de proiect).

Să se ruleze acest script pentru a se crea resursele necesare rulării atât a serverului cât și a clientului.

f) Să se actualizeze configurația de rulare a serverului:

  1. acesta va include la cale biblioteca conținând definițiile de clase necesare expunerii noii funcționalități;
  2. proprietatea java.rmi.server.codebase va referi, de asemenea, definițiile de clase corespunzătoare noii funcționalități, care vor fi încărcate astfel încât să fie accesibile la distanță.

6. Să se construiască un client care să acceseze numai funcționalitățile corespunzătoare interfeței BookPresentationManagerInterface.

Acesta:

  • va crea un sistem de securitate, în cazul în care nu există;
  • va obține o referință către rmiregistry, folosind adresa IP transmisă ca parametru la rulare;
  • va căuta serviciul BookPresentationManagerInterface accesibil prin intermediul aceleiași denumiri, primind o referință către obiectul delegat care încapsulează funcționalitățile ce pot fi invocate la distanță;
  • va defini un meniu în mod text pentru a se putea apela metodele pe care interfața le expune.

Să se relanseze în execuție serverul, prin intermediul noii configurații de rulare. Să se testeze funcționalitățile disponibile în cadrul interfeței BookPresentationManagerInterface.

7. (opțional) Să se expună metodele corespunzătoare clasei WriterManager, astfel încât acestea să fie accesibile la distanță.

8. (opțional) Să se construiască un client care să acceseze numai funcționalitățile corespunzătoare interfeței WriterManagerInterface.

Să se relanseze în execuție serverul, prin intermediul noii configurații de rulare. Să se testeze funcționalitățile disponibile în cadrul interfeței WriterManagerInterface.

Resurse

Soluții

laboratoare/laborator03.txt · Last modified: 2015/12/01 15:07 by Andrei Roșu-Cojocaru
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0