Obiecte distribuite (RMI) 1

1. Obiectivul lucrării 1

2. Noţiuni teoretice. 1

3. Paşii pentru implementarea în java a obiectelor distribuite. 2

4. Exemplu aplicaţie RMI. 4

Exerciţii 8

 

Obiecte distribuite (RMI)

 

1. Obiectivul lucrării

 

Scopul lucrării de faţă este de a prezenta arhitectura tehnologie Remote Server Invocation şi modul de utilizare pentru a implementa aplicaţii distribuite.

 

2. Noţiuni teoretice

           

Tehnologia RMI (Remote Method Invocation) este una dintre componentele fundamentale ale java, fiind introdusă începând cu versiunea jdk 1.1 . Cu ajutorul RMI părţi ale unui program pot exista pe maşini diferite , acest lucru fiind transparent din puct de vedere al programului. RMI este una dintre componentele fundamentale care stă şi la baza  Enterprise Java Beans.

 

Programarea distribuită este benefică în foarte multe situaţii. Pe măsură ce aplicaţiile cresc în dimensiuni, complexitate şi resurse necesare, programarea într-un mediu distribuit duce la creşterea performanţelor şi posibilităţilor de administrare a acestor aplicaţii.

 

O aplicaţie RMI poate fi privită ca fiind compusă din două programe : un program client şi un program server. Programul server crează unul sau mai multe obiecte distribuite, şi aşteaptă programele client care vor invoca metodele acestor obiecte distribuite.

 

Aplicaţiile ce folosesc RMI mai sunt numite şi aplicaţii cu obiecte distribuite. Într-o astfel de aplicaţie pot fi identificate următoarele activităţi ce au loc:

-          Localizarea obiectelor distribuite – aplicaţia foloseşte diverse mecanisme pentru a localiza obiectele distribuite şi pentru a obţine referinţe către acestea.

-          Comunicarea cu obiectele distribuite – detaliile de comunicare cu obiectele distribuite sunt gestionate de mecanismele interne ale RMI.

-          Încărcarea definiţii claselor – deoarece tehnologia RMI permite transmiterea obiectelor prin reţea, se oferă mecanisme de încărcare a claselor şi de transmitere a obiectelor prin reţea.

 

In figura de mai jos este prezentată o diagramă simplă ce ilustrează relaţia dintre client şi server într-o aplicaţie distribuita RMI.

Figura 1. Relatia client server

 

Principiul de funcţionare al mecanismului RMI este următorul: obiectele distribuite (accesibile de la distanţă) vor trebui să implementeze interfaţa Remote. Fiecărui obiect distribuit i se va asocia un nume şi va fi înregistrat de către rmirgistry. Clientul (care trebuie să cunoască numele cu care a fost înregistrat obiectul în rmiregistry) va primi o referinţă către obiectul distribuit, după care, va lucra cu acesta ca şi cum acesta ar fi un obiect local. Toate detaliile despre ceea ce se întâmplă de fapt în momentul în care se apelează o metoda a obiectelor distribuite, sunt invizibile pentru client şi pentru server, si sunt înglobate în cadrul claselor Stub şi Skeleton.

 

Figura 2. Aplicaţie RMI.

 

În figura 2 sunt reprezentate toate componentele implicate în cadrul unei aplicaţii distribuite ce foloseşte tehnologia RMI.

3. Paşii pentru implementarea în java a obiectelor distribuite

 

Aplicaţia server

 

Se defineşte interfaţa pe care o implementează obiectele distribuite. Această interfaţă va conţine metodele ce vor putea fi apelate de la distanţă prin intermediul mecanismelor RMI. Interfaţa va trebui să extindă interfaţa RemoteInterface. Meotdele definite în cadrul aceste interfeţe vor trebui sa aibă adăugată clauza throws RemoteException.

 

Construirea clasei ce implementează interfaţa definită la pasul anterior. Această clasă trebuie să extindă clasa UnicastRemoteObject.

 

Pe baza clasei definite la pasul anterior pot fi construite instanţe de obiecte ce vor putea fi accesate de la distanţa. Pentru a putea face accesibil de la distanţă un obiect prin intermediul mecanismului RMI trebuiesc realizaţi următorii paşi suplimentari:

a)     Instalarea în cadrul aplicaţiei ce urmează a construi şi înregistra obiecte distribuite a unui manager de securitate folosind clasa java.rmi.RMISecurityManager.

b)     Lansarea în execuţie fie din consolă, fie direct din cadrul aplicaţiei a utilitarului rmiregistry. Lansarea de la consolă se face executând comanda rmiregistry port (unde port reprezintă portul pe care utilitarul va aştepta conexiuni – sau cereri de accesare a obiectelor distribuite). Lansarea din cadrul aplicaţiei se face cu instrucţiunea LocalRegistry.createRegistry(port) (unde port are aceiaşi semnificaţie ca şi în cazul lansării de la consolă).

Paşii pentru a construi şi iniţializa un obiect distribuit sunt următorii:

a)     Construirea obiectului pe baza clasei ce implementează interfaţa RemoteInterface

b)     Înainte de a putea instanţia si a face vizibile în reţea obiecte distribuite, pe baza clasei definite la pasul anterior trebuiesc generate clasele Stub şi Skeleton folosind utilitarul rmic.exe. Cele două clase vor fi generate de către utilitarul rmic.exe pe baza clasei ce defineşte structura obiectelor distribuite. Aceste două clase implementează mecanismele de transmitere la distanţa a apelurilor de metode şi a răspunsurilor metodelor şi vor trebui să se regăsească şi în classpath-ul aplicaţiei client ce încearcă să apeleze un obiect distribuit.

c)      Înregistrarea obiectului în cadrul rmiregistry pentru a puea fi accesat de la distanţă – Naming.rebid(„//numecalc:port/numeodiec); - unde numecalc reprezintă numele calculatorului unde se afla startat rmiregistry, port reprezintă portul pe care este lansat în exectuţie rmiregistri şi numeobiect reprezintă numele prin intermediul căruia obiectul este recunoscut în cadrul rmiregistry şi va putea fi invocat de la distanţă.

 

Aplicaţia client

 

Pentru ca o aplicaţie client să obţină o referinţă către un obiect distribuit trebuie să  instaleze un manager de securitate de tip java.rmi.RMISecurityManager.

 

După instalarea managerului de securitate clientul poate obţine o referinţă către un obiect distribuit folosind o instrucţiune de froma:

ObjectInterface obj = Naming.lookup(„//host:port/name”);

 

Unde host reprezintă numele calculatorului pe care  obiectul distribuit este înregistrat, port reprezintă portul pe care utilitarul rmiregistry este lansat in execuţie pe maşina server şi name reprezintă numele sub care obiectul a fost înregistrat în cadrul serverului.

 

După obţinerea referinţei către obiectul distribuit, aplicaţia client va putea manipula respectivul obiect exact în acelaşi mod ca şi obiectele locale, fiind transparent faptul că apelul metodelor din obiectul distribuit sunt de fapt transmise la distanţă prin intermediul mecanismelor RMI.

 

Mecanismele interne care se ocupă cu apelarea la distanţă a metodelor distribuite şi întoarcerea rezultatelor metodelor apelate sunt implementate în cadrul claselor Stub şi Skeleton ce sunt generate de către compilatorul rmic.exe. Datorită acestui fapt cele două clase trebuie să fie accesibile în cadrul aplicaţiei client ce încearcă să apeleze un obiect distribuit.

 

           

4. Exemplu aplicaţie RMI

 

Aplicaţia ilustrează modul în care tehnologia RMI poate fi folosită pentru a implementa un sistem care să permită controlarea de la distanţă a unor resurse. In cazul de faţă sa implementat o aplicaţie ce permite modificarea de la distanţă a algoritmilor de control pentru un set de controlere. In figura 3 este descrisă arhitectura generală a aplicaţiei.

Figura 3. Arhitectura generala aplicaţie RMI

 

Aplicaţia este formată din două componente: componenta Control local care acţionează ca un server şi componenta Control la distanţă care acţionează ca şi un client.

 

Componenta server permite iniţializarea şi instalarea a unuia sau mai multor controlere. Prin intermediul mecanismului RMI aplicaţia server permite instalarea în cadrul controlerelor de la distanţă, de către aplicaţia client a algoritmilor de control pentru fiecare dintre controlerele instalate.

 

Componenta client defineşte unul sau mai mulţi algoritmi de control pe care îi poate instala în cadrul controlerelor aflate la distanţă prin intermediul mecanismului RMI.

 

Aplicaţia exemplifică modul în care mecanismul RMI permite transmiterea la distanţă de cod executabil.

 

In figura 4 este prezentată diagrama UML a claselor pentru aplicaţia server.

 

 

Figura 4. Diagrama UML a claselor pentru aplicaţia server

 

In cadrul aplicaţiei server clasa ControlersServer este responsabilă cu iniţializarea mecanismelor RMI, pentru a putea înregistra obiecte distribuite (figura y).

 

        if(System.getSecurityManager()==null){

            System.setSecurityManager (new RMISecurityManager ());

        }

       

       Registry  reg = LocateRegistry.createRegistry (rmiport);

Figura y. Secvenţa de cod pentru initializare security manager şi rmiregistry

 

De asemenea clasa ControlersServer defineşte metoda registreControler (ControlerEngine ctr) prin intermediul căreia un controler este înregistrat în sistem (folosind metoda Naming.rebind(...)) şi poate fi accesat de la distanţă, şi metoda unregistreControler(String ctrlName) prin intermediul căreia un controler poate fi şters din sistem (folosind metoda Naming.unbind(...)).

 

Clasa ControlEngine defineşte structura obiectelor ce vor putea fi apelate de la distanţă, ea implementând interfaţa Controler în cadrul căreia sunt declarate metodele accesibile de la distanţă. Această clasă este de tip fir de execuţie şi permite rularea în cadrul firului a unui algoritm de control. Setarea algoritmului de control se face prin intermediul metodei setAlgorithm(ControlAlgorithm alg) care poate fi apelată de la distanţă de către clienţi distribuiţi. Clientul are posibilitatea de a defini proprii algoritmi de control, şi de a-i instala în cadrul controlerului, el trebuind să apeleze la distanţă metoda setAlgorithm(ControlAlgorithm alg) transmiţând ca parametru al metodei un obiect ce implementează interfaţa ControlAlgorithm.

 

In figura 5 este prezentată structura metodei run() ce se execută în cadrul firelor de tip ControlerEngine.

 

public void run(){

        nextState = RUNNING;

       

        while(active){

            synchronized(look){

                algorithm.executeStep();

                if(nextState == PAUSED)

                    try {

                        look.notify();

                        look.wait();

                        nextState = RUNNING;

                    } catch (InterruptedException e1) {                  

                        e1.printStackTrace();

                    }

            }

            try{Thread.sleep(1000);}catch(Exception e){e.printStackTrace();}

        }

      }

Figura 5. Metoda run() din cadrul clasei ControlEngine.

 

In figura 6 este prezentă metoda setAlgorithm(ControlAlgorithm alg) apelabilă de la distanţă de către clienţi distribuiţi prin intermediul mecanismului RMI.

public void setAlgorithm(ControlAlgorithm alg) {

        if(controlerThread==null){

            controlerThread = new Thread(this);

            controlerThread.start();

        }

        else{

            nextState = PAUSED;     

                    synchronized(look){

                        try {

                            look.wait();                             

                        } catch (InterruptedException e) {               

                            e.printStackTrace();

                        }

                    }

       

        }//.else

       

        synchronized(look){

            this.algorithm = alg;

            nextState = RUNNING;

            look.notify();

        }

    }

Figura 6. Metoda setAlgorithm(ControlAlgorithm alg) din cadrul clasei ControlEngine, apelabilă de la distanţă.

 

In figura 7 este prezentată diagrama UML a claselor aplicaţiei client.

Figura 7. Diagrama UML a claselor pentru aplicaţia client.

 

Aplicaţia client implementează unul sau mai mulţi algoritmi de control pe care îi poate încărca în cadrul controlerelor ce rulează în alte locaţii. Comunicaţia între client şi controler se face prin intermediul mecanismului RMI, aplicaţia client putând obţine o referinţă a unui controler, pentru care apoi să seteze un alt algoritm de control prin apelarea metodei setAlogirthm(ControlAlgorithm).

 

In figura 8 este prezentate secvenţele de instrucţiuni necesare pentru un client pentru a seta un algoritm pe n controler distribuit.

 if (System.getSecurityManager() == null) {

                System.setSecurityManager(new RMISecurityManager());

 }

Controler ctrl = (Controler)Naming.lookup(remoteObjectName);

ctrl.setAlgorithm(new SimpleControlAlgorithm(args[1]));

Figura 8. Secvenţă de instrucţiuni pentru setarea unui algoritm de control.

 

Exerciţii

Importaţi în mediul Eclipse proiectul ce exemplifică noţiunile prezentate în acest laborator (link proiect). Aplicaţia pentru acest proiect este formată din două proiecte: un proiect pentru aplicaţia server RMI şi un proiect pentru aplicaţia client RMI.

Compilati si executati aplicatiile server si client RMI.

Compilare aplicatie server

  1. Compilati aplicatia server (direct din mediul Eclipse sau din linia de comanda).
  2. Generarea fisierului stub folosind utilitarul rmic. Din directorul radacina al proiectului laborator4_rmi_server se executa comanda:

rmic –v1.2   –classpath . lab.scd.rmi.server.RouteFinderEngine

Compilare aplicatie client

  1. Compilati aplicatie client (direct din mediul Eclipse sau din linia de comanda).

Lansare in executie aplicatie server.

  1. Adaugati in directorul radacina al proiectului server fisierul cu numele java.policy cu urmatorul continut:

grant {

    permission java.net.SocketPermission "*:1024-65535",

        "connect,accept";

   permission java.io.FilePermission

        "c:\\java\\eclipse\\workspace\\laborator4_rmi_client\\-", "read";

    permission java.io.FilePermission

        "c:\\java\\eclipse\\workspace\\laborator4_rmi_server\\-", "read";

};

  1. Adaugati in directorul radacina al proiectului fisierul startserver.bat cu urmatorul continut:

java -cp . -Djava.rmi.server.codebase=file:/c:\java\eclipse\workspace\laborator4_rmi_server/ -Djava.rmi.server.hostname=localhost -Djava.security.policy=java.policy lab.scd.rmi.server.RouteFinderEngine

  1. Lansati in executie aplicatia server executand startserver.bat

 Lansare in executie aplicatie client.

  1. Adaugati in directorul radacina al proiectului client fisierul java.policy cu urmatorul continut

grant {

    permission java.net.SocketPermission "*:1024-65535",

        "connect,accept";

    permission java.io.FilePermission

        "c:\\java\\eclipse\\workspace\\laborator4_rmi_client\\-", "read";

    permission java.io.FilePermission

        "c:\\java\\eclipse\\workspace\\laborator4_rmi_server\\-", "read";

};

  1. Adaugati in directorul radacina al proiectului fisierul startclient.bat cu urmatorul continut:

java -cp . -Djava.rmi.server.codebase=file:/c:\java\eclipse\workspace\laborator4_rmi_server/ -Djava.security.policy=java.policy lab.scd.rmi.client.RmiClient localhost

  1. Lansati in executie aplicatia client executand startclient.bat

Exrciţiul 2

 

Construiţi o aplicaţie RMI pe baza descrierii aplicaţiei date în cadrul paragrafului Exemplu aplicaţie RMI.