Prezentarea Metodei Rmi In Limbajul Java

1. Introducere

Metoda RMI(remote method invocation), adica invocarea metodei de la distanta, furnizeaza stratul intermediar in retea care permite obiectelor Java rezidente pe masini virtuale diferite sa comunice folosind metodele obisnuite de apelare. Atât timp cât calculatoarele gazda pot sa comunice prin TCP/IP (de exemplu, calculatoare conectate la Internet), aplicatiile de retea pot fi dezvoltate fara streamuri(fluxuri de date) si socketuri(puncte de legatura).

Acest lucru permite programatorului sa evite protocolurile de comunicare complexe între aplicatii si în schimb sa adopte un proiect de nivel mai înalt al metodei bazate.

Intregul scop al implementarii metodei RMI este sa furnizeze un cadru pentru obiectele Java, pentru a comunica prin metodele lor, fara a depinde de locul unde sunt situate. Aceasta inseamna ca un client ar trebui sa acceseze un server de pe masina locala sau de pe retea ca si cum ei ar fi în acelasi sistem in momentul executiei. Din punctul de vedere al programatorului, detaliul de retea necesar pentru aplicatiile distribuite dispare. Toate comunicarile de retea sunt executate în mod transparent, sub aparenta apelarilor standard ale metodei.

Pentru a crea o clasa care va fi accesibila de la distanta, definim mai întâi o interfata care declara acele metode pe care noi dorim sa le facem publice. Parametrii si valorile returnate pot sa fie de orice tip; transferul de date este manipulat de streamurile(fluxurile de date) obiectelor în mod automat. Clasa trebuie sa puna în aplicare aceasta interfata, plus oricare alte interfete si metode de care ea are nevoie pentru folosirea ei in scopul propus.

O clasa stub si o clasa skeleton trebuiesc dupa aceea sa fie generate folosind comanda rmic, o unealta disponibila în JDK. Un stub este o clasa care în mod automat traduce apelari de la distanta ale metodei in parametrul trecator în configurarea comunicarii in retea . Un skeleton este clasa corespondenta care este rezidenta pe masina virtuala de la distanta si care accepta aceste conexiuni de retea si le traduce in apelari reale de metoda pe obiectul real.

Setarea finala implicata in folosirea RMI-ului este ca acel obiect de la distanta trebuie sa fie înregistrat cu un nume de serviciu care permite clientilor sa il localizeze in timp ce ruleaza. Clientul se conecteaza la numele registrului si solicita o referire despre un obiect care s-a înregistrat sub un nume de botez (ca de exemplu DateServer). Dupa aceea numele registrului întoarce o referire de la distanta despre obiectul inregistrat sub acel nume.

Sub arhitectura actuala RMI, aceasta referire include gazda unde obiectul ruleaza de la distanta, portul pe care el asculta si identificatorul obiectului intern RMI. Toate aceste detalii sunt oricum ascunse programatorului. Programatorul pur si simplu primeste o clasa stub care pune în aplicare interfetele necesare si în mod automat traduce apelarile de metoda în apelari de metoda de la distanta pe obiectul real.

Dupa ce clientul a obtinut o referire de la distanta, el poate sa inceapa sa invoce metodele de pe obiectul aflat la distanta. Cadrul RMI are grija de toate comunicarile de la cel mai jos nivel; clientul este capabil sa faca apelari de metoda pe obiectul aflat la distanta ca si cum ar face apelarile pe obiect local. Cadrul RMI chiar include si suportul pentru exceptiile de la distanta care sunt returnate pentru colectia de gunoi distribuita, pentru ca obiectele de la distanta sa nu fie strânse in gunoi în timp ce referirile de la distanta ramân lor.

Urmarind apelarea metodei clientul pare sa invoce metoda de pe obiectul de la distanta direct. Apelarea este transferata stub-ului, care stapâneste toate detaliile despre configurarea comunicarii si transmite apelarea si parametrii la skeleton. Dupa aceea skeleton-ul transforma apelul de metoda de pe obiectul aflat la distanta. Valoarea returnata este trimisa în cele din urma înapoi de la skeleton la stub, ca si cum apelul de metoda ar fi fost facut local. Exceptii desigur sunt si ele returnate înapoi pe masura ce rezultate sunt corecte.

Daca obiectul de la distanta a fost configurat în mod corect, atunci toata aceasta munca este transparenta ambilor, adica la obiectului aflat la distanta si clientului. Acesta nu însemna ca acele obiecte pot fi transformate în obiecte aflate la distanta fara o cunoastere anterioara si sigura; interfetele specifice trebuie sa fie puse în aplicare si fisierele de clasa specifice trebuie sa fie generate. De asemenea nu însemna ca oricare metoda a obiectului de la distanta poate sa fie invocata; doar acele metode declarate în interfata de la distanta sunt accesibile clientilor. Oricum, tin^nd cont de aceste diferente, cadrul RMI este un mecanism extrem de puternic pentru ca el permite aplicatiilor de retea sa fie dezvoltate, ca si cum totul ar fi o apelare de metoda de la distanta.

1.1 Obiectul de la distanta

Un obiect de la distanta este oricare obiect care a fost setat sa accepte apelari de metoda de la un alt obiect care ruleaza pe o masina Java virtuala aflata la distanta. Acesta este format din doua parti: o interfata care descrie metodele obiectului si implementarea acestei interfete.

1.1.1Definirea interfetei de la distanta

O interfata trebuie sa fie scrisa pentru obiectul de la distanta definind toate metodele care ar trebui sa fie publice. Aceasta interfata trebuie sa extinda java.rmi.Remote, o clasa care pur si simplu identifica de la distanta interfetele accesibile.

De exemplu, pentru a scrie un obiect de la distanta cu o singura metoda, interfata poate arata astfel:

import java.rmi.Remote;

import java.rmi.RemoteException;

public interface MyRemote extends Remote

{

public int RemoteHash (String s) throws RemoteException;

T

In exemplul de mai sus, noi declaram o interfata, care poate sa fie implementata cu oricare obiect aflat la distanta si care declara o singura o metoda accesibila de la distanta, remoteHash(), care primeste un parametru de tip string si returneaza un parametru de tip int. Toate acele metode care sunt declarate într-o interfata de la distanta trebuie sa fie declarate astfel inc^t ele sa genereze toate exceptiile de tipul java.rmi.RemoteException. O metoda poate, desigur, sa fie declarata ca ea sa poata sa genereze alte exceptii, depinz^nd de propria ei alegere.

1.1.2 Implementarea interfetei de la distanta

Implementarea obiectului de la distanta trebuie sa extinda java.rmi.server.RemoteObject sau o subclasa, si trebuie sa puna în aplicare fiecare din interfetele de la distanta pe care obiectul doreste sa le suporte. În realitate, java.rmi.server.Unicast.RemoteObject este cea mai acceptabila pentru a fi folosita ca supraclasa; aceasta clasa furnizeaza o implementare a comportarii tuturor obiectelor caracteristice de la distanta.

O implementare a interfetei de mai sus ar trebui sa arate astfel:

import java.rmi.*;

import java.rmi.server.*;

public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote

{

public MyRemoteImpl () throws RemoteException {

T

public int remoteHash (String string)

{

return string.hashCode();

T

T

Noi extindem UnicastRemoteObject si punem în aplicare interfata de la distanta pe care noi tocmai am declarat-o. Sa remarcam ca constructorul pentru supraclasa UnicastRemoteObject poate sa genereze exceptia de tipul RemoteException, asa ca noi trebuie de asemenea sa declaram un constructor care permite exceptii de acest tip.

1.2 Clasele Stub si Skeleton

Obiectele stub si skeleton trebuie sa fie generate apoi cu unealta rmic. Aceasta unealta ia numele implementarii clasei ca un parametru si genereaza doua fisiere de tip class: unul pentru stub si unul pentru skeleton.

rmic –d . MyRemoteImpl

Acesta comanda creeaza fisierele MyRemoteImpl_Stub.class si MyRemoteImpl_Skel.class. Ambele fisiere sunt necesare pentru a executa implementarea obiectului de la distanta (serverul). Un client a obiectului de la distanta, oricum, are nevoie doar sa acceseze fisierul de clasa stub.

Clientul stub autogenerat si obiectul skeleton de la distanta contin toate codurile implicate în detaliile de distribuire seriala a unei apelari de metoda de la distanta. Invocând dinamic distribuirea seriala a metodei de la distanta, rezultatul este returnat inapoi la apelant. Pentru a interpreta transferul de date, clasele stub si skeleton folosesc o clasa personalizata numita ObjectOutputStream; Livrarea este executata peste conexiunea TCP/IP, si omiterea de date este executata cu o clasa personalizata numita ObjectInputStream. Sa remarcam ca

acel obiect trecator are întotdeauna valoare, ca si cum am fi folosit stream-urile de obiect direct.

1.3 Clasa RemoteException

RemoteException este supraclasa tuturor exceptiilor care se pot întâmpla în timpul rularii RMI-ului. Acesta exceptie este generata oricând o invocare a metodei de la distanta este neacceptata.

Toate metodele dintr-o interfata de la distanta trebuie sa declare ca clasele stub si skeleton pot sa genereze acest tip de exceptie.

1.4 Numele registrului

Clientii pot sa invoce metodele de pe obiectele aflate la distanta numai daca ei au o referire despre obiectul aflat la distanta. Un simplu nume de registru este furnizat în cadrul RMI de unde se obtin referintele. Numele registrului poate fi pornit manual cu comanda rmiregistry sau prin program cu metodele clasei LocateRegistry, pe care o sa le discuam mai t^rziu.

Obiectele de la distanta pot sa fie inregistrate folosind clasa java.rmi.Naming folosind un URL cum ar fi numele unei scheme. Sa remarcam ca numele registrului este el însusi un simplu client RMI, asa ca el trebuie sa aiba acces la fisierele stub-ului pentru clasele care vor fi înregistrate în aceasta privinta; asa ca trebuie sa fie executat impreuna cu calea fisierelor stub-ului, care se afla in CLASSPATH, sau sa fie în stare sa foloseasca mecanismul de distribuire de clasa automata RMI.

Când un obiect se înregistreaza cu numele lui de registru, el trebuie sa specifice un nume sub care ar trebui sa fie afisat. Clientii dupa aceea pot sa foloseasca clasa java.rmi.Naming pentru a obtine o referire de la distanta despre obiectul din registru. Obiectul este pur si simplu identificat cu URL-ul care contine numele gazdei pe care nume registrului ruleaza si numele sub care obiectul este afisat.

2. Un server RMI de date

Acest exemplu demonstreaza invocarea de metoda de la distanta de la un server care permite clientilor sa determine data si timpul la care serverul lucreaza folosind apelarile de metoda de la distanta.

Pentru a crea aplicatia server, trebuie sa executam un numar de pasi. Aceasta este o lista generala de pasi pe care ii folosim când implementam orice aplicatie care foloseste RMI-ul:

Definim interfata de la distanta. Aceasta este interfata prin care clientii de la distanta vor accesa serverul.

Implementam interfata de la distanta. Apelarile de metoda de la distanta de la clienti vor fi facute in cele din urma pe aceasta implementare. Aceasta ar trebui sa se înregistreze într-un nume de registru pentru ca sa poata sa fie localizata de la distanta.

Generam clasele stub si skeleton folosind comanda rmic.

Scriem un client care localizeaza serverul în numele de registru si dupa aceea apelam metodele de la distanta.

Pornim numele registrului folosind rmiregistry. Alternativ, serverul poate porni registrul el însusi.

Pornim serverul.

Executam clientul.

2.1 Interfata serverului de date

Primul pas este sa scriem o interfata care descrie toate metodele pe care noi vrem ca clientul sa fie în stare sa le invoce. În acest caz avem o singura metoda pe care trebuie sa o facem publica, si ea va întoarce un obiect de tip Date.

import java.rmi.Remote;

import java.rmi.RemoteException;

import java.util.Date;

public interfmplementam interfata de la distanta. Apelarile de metoda de la distanta de la clienti vor fi facute in cele din urma pe aceasta implementare. Aceasta ar trebui sa se înregistreze într-un nume de registru pentru ca sa poata sa fie localizata de la distanta.

Generam clasele stub si skeleton folosind comanda rmic.

Scriem un client care localizeaza serverul în numele de registru si dupa aceea apelam metodele de la distanta.

Pornim numele registrului folosind rmiregistry. Alternativ, serverul poate porni registrul el însusi.

Pornim serverul.

Executam clientul.

2.1 Interfata serverului de date

Primul pas este sa scriem o interfata care descrie toate metodele pe care noi vrem ca clientul sa fie în stare sa le invoce. În acest caz avem o singura metoda pe care trebuie sa o facem publica, si ea va întoarce un obiect de tip Date.

import java.rmi.Remote;

import java.rmi.RemoteException;

import java.util.Date;

public interface DateServer extends Remote

{

public Date getDate() throws RemoteException;

T

Extindem interfata Remote pentru ca punem în aplicare o interfata de la distanta. Metoda getdate() trebuie sa fie declarata sa poata genera RemoteException, care se pot întâmpla în timpul oricarei invocari de metoda de la distanta.

2.2 Clasa DateServImpl(implementarea interfetei)

Al doilea pas este sa scriem o implementare a interfetei obiectului de la distanta. Extindem UnicastRemoteObject si implementam DateServer.

Toate implementarile obiectelor de la distanta trebuie sa extinda clasa RemoteObject sau una din subclasele lui. Clasa UnicastRemoteObject este furnizata de JDK si este o implementare folositoare pentru a folosi configurarea client/server a retelei TCP . Aceasta clasa se pregateste în mod automat sa accepte conexiunile RMI pentru apelarile de metoda de la distanta când se apeleaza constructorul.

import java.rmi.Remote.*;

import java.rmi.RemoteException.*;

import java.util.Date;

public class DateServerImpl extends UnicastRemoteObject implements DateServer

{

// public DateServerImpl () throws RemoteException…

// public Date getDate () …

// public static void main (Stringst args)…

T

2.3 Generarea claselor stub si skeleton

Pentru a genera clasele stub si skeleton, compilam clasa DateServImpl si apoi folosim comanda rmic av^nd ca parametru numele clasei care implementeaza serverul de date :

rmic DateServImpl

Doua fisiere de tip class vor fi generate: unul pentru stub care se cheama DateServerImpl_stub si unul pentru skeleton care se cheama DateServerImpl_skel.

2.4 Clasa DateClient

Acesta clasa numita DateClient cauta serverul de date în numele registrului, strânge o referire despre el, face o apelare la metoda getDate() si dupa aceea afiseaza rezultatul.

import java.rmi.Naming;

import java.util.Date;

Public class DateClient

{

public static void main (string st args) throws Exception

{

if (args.length != 1)

throw new IllegalArgumentException (

“Syntax : DateClient <hostname> );

DateServer dateServer = (DateServer) Naming.lookup

(“RMI ://” + argss0t + “/Date Server”);

Date when = dateServer.getDate ();

System.out.println (when) ;

T

T

Clientul are numai metoda numita main(), care verifica argumentele, localizeaza serverul de la distanta, si determina data la care serverul ruleaza.

Metoda lookup() din clasa Naming este folosita pentru a primi o referire despre implementarea de la distanta a serverului DateServer. Este folosit un URL cu câmpul de protocol rmi pentru a mentiona acest obiect; partea rmi poate sa fie omisa, asa ca putem sa folosim numai URL-ul //server/object.

URL-ul specifica doua lucruri: masina pe care numele registrului ruleaza si numele cu care obiectul a fost înregistrat acolo. În acest caz, gazda este specificata in linia de comanda (gazda este folositoare pentru testare) si numele obiectului este Date Server.

2.5 Executia aplicatiei

Pentru a rula exemplul, pornim registrul de pe masina server folosind comanda:

rmiregistry

Dupa aceasta comanda registrul este accesibil clientilor de la distanta pe portul standard binecunoscut. De obicei trebuie sa pornim registrul dintr-un director care contine clasele stub-ului pentru obiectele care vor fi înregistrate. Apoi, pornim serverul tot pe aceeasi masina folosind comanda:

java DateServImpl

Când rulam acesta clasa este creat un exemplu al clasei DateServerImpl si înregistrat in registrul local. Sa remarcam ca registrul trebuie sa ruleze pe masina server înainte ca aceasta înregistrare sa apara. În cele din urma, rulam clientul de pe oricare alta masina folosind comanda:

java DateClient localhost

Când rulam clientul, trebuie sa specificam numele calculatorului gazda pe care numele registrului ruleaza. Clientul se conecteaza la numele registrului; obtinem o referire despre obiectul de la distanta si apelam metoda getdate(); metoda returneaza data actuala de pe masina server.

În culise, clasa Naming se conecteaza la registrul care ruleaza pe gazda specificata si dupa aceea întreaba despre obiect, egalând numele specificat.

Obiectul returnat cu acesta apelare este o referire de la distanta (de fapt un exemplu al stub-ului) care pune în aplicare oricare dintre interfetele de la distanta care au fost puse în executie cu serverul de la distanta. De aceea nu trebuie sa distribuim rezultatul la interfata de la distanta si implementarea clasei cu numele de la distanta: Numai metodele declarate în interfetele de la distanta sunt accesibile peste RMI.

Când s-a obtinut o referire de la distanta despre obiect putem sa invocam metodele declarate în interfetele lui de la distanta. În acest caz, apelam metoda getdate() si tiparim raspunsul. Când facem acesta apelare de metoda stub-ul local se conecteaza la skeleton-ul de la distanta care apeleaza metoda obiectului de la distanta, obtinem un rezultat, care curge inapoi la stub. Stub-ul il returneaza clientului pentru afisare.

3. Amplasarea fisierelor de tip class

Când construim aplicatii bazate pe RMI, sfârsim cu cinci seturi de clase principale: clasele de implementare a clientului, clasele de implementare de serverului, interfetele de la distanta, clasele stub-ului, clasele skeleton-ului.

Pentru a distribui un client RMI, de exemplu, amplas^ndu-l pe un server Web pentru acces de catre un browser Web, trebuie sa includem: clasele de implementare ale clientului (acestea sunt clasele care vor fi rulate de client), clasele interfetei de la distanta ( acestea sunt clasele care descriu cum clientul va accesa serverul) si clasele de stub-ului (aceste sunt clasele care transforma apelarile de metoda de la distanta ale clientului în conexiuni de retea).

Pentru a distribui un server RMI, de exemplu, instalându-l pe un server Web, trebuie sa includem: clasele de implementare ale serverului, interfetele de la distanta, clasele stub-ului si clasele skeleton-ului. Desi clasele stub-ului sunt executate numai de clienti, ele sunt de asemenea necesare serverului când exporta accesul la distanta.

Unele aplicatii RMI vor, desigur, sa nu desparta clientul de server mai mult dec^t at^t. Unele dintre fortele marete ale RMI-ului este acel server poate sa faca apelarile de metoda de la distanta de pe client, asa cum clientul poate sa faca apelarile de metoda de la distanta de pe server. Într-adevar, aplicatiile mufa-la-mufa care sa nu aiba deosebire între punctele de sfârsit sunt posibile cu RMI.

Cadrul RMI include de asemenea capacitatea de distribuire automata a implementarii fisierelor clasei mentionate cum ar fi fisierele stub-ului. Folosind acest mecanism o aplicatie RMI are nevoie sa acceseze numai fisierele specifice clasei lui si oricare din interfetele de la distanta pe care le foloseste. Oricare alte fisiere ale unei clase care apar ca parte a unei apelari de metoda de la distanta vor fi aduse în mod automat. Când aplicatia obtine o referire despre un obiect de la distanta, el va aduce în mod automat fisierele stub-ului obiectului de la o adresa corespunzatoare. Aceasta capacitate este discutata mai amanuntit în continuare.

Principalul avantaj al acestei capacitati este ca un client nu are nevoie sa stie ce clasa implementeaza interfata de la distanta; el are nevoie doar sa stie ca interfata de la distanta este implementata de obiectul de la distanta. Stub-ul corespunzator pentru obiectul de la distanta va fi adus în mod automat si transparent impreuna cu oricare alte clase care sunt implicate în implementarea obiectului dar nu au contact direct cu clientul.

3.1 Distribuirea automata a fisierelor class

Unul dintre limitele RMI-ului este ca clientul si numele registrului trebuie sa aiba acces la fisierul stub-ului pentru existenta unui serviciu accesat de la distanta si ca cererile serverului trebuie sa acceseze fisierele stub-ului si skeleton-ului, chiar daca utilizatorul nu codifica niciodata accesele directe la aceste clase. Similar clientul si serverul trebuie sa aiba accesul la fisierele class ale tuturor claselor cu care ei vor schimba parametrii si valorile returnate de apelarea metodei de la distanta. De exemplu, daca un client asteapta ca o metoda de la distanta sa returneze un obiect de tipul MyDBEntry si serverul de fapt returneaza o subclasa a obiectului MyDBEntry, atunci clientul trebuie sa aiba si el acces la fisierul clasei MyDBEntry.

Acesta este o limitare inutila, pentru ca în multe cazuri clientul si serverul vor accesa clasele problematic numai printr-o interfata standard implementata de server, sau în ultimul exemplu, prin API-ul clasei DBEntry si nu va avea nevoie niciodata de a accesa direct clasele subliniate.

Din acest motiv RMI-ul contine o facilitate pentru distribuirea automata a fisierelor class. Folosind acest mecanism, când este int^lnit un nume de clasa necunoscut, cadrul RMI va aduce în mod automat fisierul de clasa corespondent de la o adresa standard. Sub JDK 1.2 acest mecanism este expus prin clasa MarshallObject, care ar trebui sa fie folositoare în alte situatii.

3.2 Incarcarea fisierelor de tip class

Când cadrul RMI (un client sau un server) întâlneste un nume de clasa pe care il recunoaste el incearca sa aduca clasa de la urmatoarele adrese:

Adresa preferata de la care oricare clasa necunoscuta poate sa fie adusa este CLASSPATH.

Daca un nume de clasa necunoscuta este întâlnit de un server ca parametru a unei apelari de metoda de la distanta sau de un client ca rezultat al unei apelari de metoda de la distanta (incluzând stub-ul de la distanta care este returnat de o apelare la numele registrului), atunci datele RMI pot sa includa URL-ul de unde fisierul clasei poate sa fie adus. Acest URL va fi de asemenea folosit pentru oricare alte clase necunoscute care au nevoie de aceasta noua clasa pe care o aducem.

In cele din urma, daca un nume necunoscut de clasa locala este int^lnit (dar nu in timpul unei apelari de metoda de la distanta; de exemplu skeleton-ul), cadrul RMI va incerca sa aduca fisierul class de la URL-ul specificat in proprietatile System din java.rmi.server.codebase. Acesta poate sa fie folosit ca adresa centrala de depozitare pentru clasele locale comune.

Pe masura ce este mai clar de la a doua optiune RMI-ul include în mod automat adresa claselor impreuna cu numele claselor care se transmit. El determina aceste adrese dupa cum urmeaza:

Daca un nume de clasa este transmis ca parte a datelor RMI si aceasta clasa este deja adusa de la o adresa de la distanta (ca de exemplu serverul Web), atunci adresa clasei particulare de la distanta este inclusa in datele RMI.

Altfel, daca clasa a fost adusa de la sistemul de fisier local proprietatea sistem java.rmi.server.codebase (daca este setata) este transmisa ca o adresa de la care fisierul class poate sa fie adus.

Pentru a permite unui client RMI sa aduca în mod automat fisierele stub-ului pentru serverul RMI, trebuie astfel sa specificam un codebase de la care fisierele class sa poate fi aduse. De exemplu:

java –Djava.rmi.server.codebase=

=http://nitric.com/jnp/classes/ServerImpl

URL-ul specificat va fi dupa aceea folosit de clienti pentru a aduce fisierul stub pentru server si oricare alte clase de care are nevoie clientul dar de fapt nu le acceseaza direct. Trebuie sa copiem manual diversele fisiere class diverse la aceasta adresa pe serverul Web.

Fundamental, daca o noua clasa, pe care RMI-ul nu a folosit-o, este folosita ca rezultat a unei apelari RMI, atunci fisierul class poate sa fie adus de la distanta. Altfel, el va fi adus folosind mecanismele normale Java.

De exemplu, daca clientul asteapta un obiect numit Runnable care sa fie întors de o apelare de metoda de la distanta dar serverul returneaza o subclasa numita MyRunnable, fisierul clasei Runnable va fi în mod automat adus de la codebase-ul serverului daca el nu este prezent în codebase-ul standard al clientului.

Similar, daca un server asteapta un obiect Runnable si clientul prezinta o subclasa MyRunnble ca un parametru, serverul va încerca sa aduca fisierul clasei MyRunnble de la codebase-ul clientului daca el este prezent în fisierul sistemului local. În acest caz, clientul trebuie specifice codebase-ul asa cum serverul specifica in exemplul de mai sus.

Mai mult tehnica de bootstrapping este disponibila, care permite unui server sa aduca propriele fisierele stub si skeleton de la o adresa de la distanta. Aceasta tehnica si aspectele suplimentare ale RMI-ului sunt descrise mai amanutit în documentul de specificatie Javasoft's RMI, inclus in JDK.

3.3 Cosideratii de protectie

Implicit, aplicatiile nu au nici un manager de protectie astfel ca codul are acces nelimitat la resurse. Acesta este o situatie implicit periculoasa a RMI-ului si a distribuirii automate a fisierelor class pentru ca daca o apelare la o clasa necunoscuta este schimbata într-o apelare de metoda de distanta primitorul va încerca sa aduca în fisierul class de la o adresa de la distanta. Un utilizator ar putea sa numeasca un cod arbitrar în acest fisier class, si astfel, câstigam acces nedorit la resursele primitorului. Din acest motiv RMI-ul nu va aduce fisierele class de la distanta daca acolo nu este instalata SecurityManager.

Daca dorim sa sprijinim aducerea fisierelor class de la distanta atunci trebuie sa instalam SecurityManager care impune controalele accesului apropriat. Acesta ar trebui sa fie instalat înaintea expunerii unui obiect pentru accesul de la distanta. Daca managerul de protectie nu este instalat atunci SecurityException va fi ridicat în timpul unei apelari de metoda de la distanta daca este int^lnita o clasa care necesita sa fie adusa de la distanta.

Din motive evidente instalarea lui SecurityManager, care permite toate operatiile pentru clasele aduse nu este o idee buna. RMISecurityManager este o implementare simpla a SecurityManager care permite aducerea claselor stub-ului de la distanta. Oricum el interzice oricaror clase operatii sensibile ca de exemplu accesul codului, fisierele natale, si asa mai departe.

Daca dorim ca clasele aduse sa aiba nivelele alternative de acces trebuie sa instalam o subclasa a clasei RMISecurityManager schimbând întarirea protectiei pe masura corespunzatoare.

Pentru a instala un nou SecurityManager pur si simplu apelam metoda SetSecurityManager() care apartine clasei System înaintea sa pornim aplicatia RMI:

System.setSecurityManager (new RMISecurityManager ());

RemoteService remoteService = new RemoteService ();

Naming.bind (“ServiceName”, remoteService);

Pentru multe aplicatii, oricum, permiterea claselor sa fie aduse de la distanta nu este necesara. Facem acest lucru numai daca dorim sa sprijinim mecanismul de distribuire automata a fisierelor class.

Applet-urile, desigur, nu pot sa configureze un nou SecurityManager; implicit appletul SecurityManager este, oricum, influentabil de distribuirea RMI a fiserelor class: Fisierele de la distanta trebuie pur si simplu sa fie toate prezente pe serverul de la care appletul este adus.

În toate exemplele urmatoare omitem instalarea unui RMISecurityManager: folosirea distribuirii automate a fisierelor class nu este de obicei necesara.

3.4 Comportarea obiectelor

Capacitatea RMI-ului de a aduce în mod automat fisierele class pentru clasele necunoscute, pe masura ce ele sunt întâlnite, si trecerea în mod automat a adresei acestor fisiere class în alte obiecte de la distanta, are consecinte destul de interesante.

În mod traditional transmiterea unei obiect peste un protocol de retea implica pur si simplu conditiile de transmisie: numai datele incapsulate de obiect sunt transmise. Folosind RMI-ul si distribuirea automata a fisierelor class comportarea obiectelor este posibila. Transmisia unei comportari a obiectului implica transmisia comportarii obiectui si a conditiilor de transmisie. Comportarea obiectului este furnizata de catre propriul fisier class care este adus în mod automat de primitor daca nu il cunoaste deja.

Comportarea obiectelor nu este unica RMI-ului. El este de obicei comparativ simplu pentru a implementa stream-urile obiectului care includ fisierul class al obiectului impreuna cu conditiile sale. Ce este important despre RMI este ca el furnizeaza acesta comportare ca comportare standard impreuna cu capacitatea de a stabili cu usurinta interconexiuni de retea. Mai mult, RMI-ul furnizeaza siguranta pentru RMISecurityManager care protejeaza primitorul de o comportare a obiectului de la activitatile potential periculoase.

4. RMI – descrierea pachetelor

Cadrul RMI este compus din câteva pachete. Vom discuta unele dintre clase lor si vom ignora altele. Pachetele principale sunt:

java.rmi Acest pachet contine clasele în legatura cu partea de client a RMI-ului. Aceste clase sunt folosite cu clienti pentru a accesa de la distanta serviciile RMI-ului. Acest pachet include interfetele prin care obiectele de la distanta sunt accesate si un mecanism pentru a localiza serviciile RMI-ului de pe o masina de la distanta.

java.rmi.server Acest pachet contine clasele în legatura cu partea de server a RMI-ului. Acest pachet include clasele suport pentru expunerea unui serviciu RMI prin TCP/IP si a celor mai apropriate cereri HTTP.

java.rmi.registry Acest pachet contine clasele în legatura cu numele registrului RMI. Cu acest pachet registrii mentionati pot fi creati, localizati, si manipulati de la distanta.

java.rmi.dgc Acest pachet contine clasele care sprijina colectia de gunoi distribuita. Acesta este mecanismul cu care colectia de gunoi Java automata este extinsa pentru distribuire. Serverele RMI mentin în mod automat o numaratoare a numarului activ de apelari de la distanta pe care le serveste si pot sa se închida când ei nu sunt sunt accesati.

java.rmi.activation Acest pachet contine sprijinul pentru mecanisul de declansare JDK 1.2. Folosind acest mecanism, serverele RMI nu au nevoie sa ruleze permanent; in schimb ei sunt activati cu o declansare numita activation daemon numai când ajunge o cerere reala. Normal ei sunt pastrati într-un conditie serializata pasiva. Acest mecanism este foarte elaborat; furnizarea detaliilor complete este peste scopul acestei lucrari.

5. Interfata Remote

Acesta interfata din pachetul java.rmi este suprainterfata pentru interfetele de la distanta. O interfata de la distanta descrie metodele pe care le suporta un obiect de la distanta. Aceste sunt singurele metod care pot sa fie accesate prin RMI de catre un client.

Când un client RMI acceseaza un obiect de la distanta, el va accesa întotdeauna obiectul prin una sau mai multe din aceste interfete de la distanta; el nu poate niciodata sa acceseze obiectul prin alte metode care sunt specifice implementarii actuale.

5.1 Semantica apelarii metodei de la distanta

Diferenta majora între o apelare de metoda de la distanta si o apelare de metoda directa este ca sub RMI parametrii sunt trecuti cu valoare; adica serverul primeste o copie a parametrilor pasati unei metode de la distanta. Daca trecem, de exemplu, parametrul Vector la o metoda si metoda de la distanta manipuleaza parametrul Vector clientul nu va vedea schimbarile reflectate în copia lui locala a parametrului Vector.

În schimb daca sub RMI dorim sa modificam parametrul Object în cadrul unei metode de la distanta, trebuie sa întoarcem parametrul Object modificat ca rezultat al apelarii metodei.

Alternativ, putem sa punem în aplicare structura pentru a fi manipulata ca un obiectul real de la distanta. Când trecem o referire la aceast obiect într-o apelare de metoda de la distanta primitorul va primi o referire de la distanta la obiectul original si astfel oricare schimbari pe care i le facem vor fi transportate înapoi la implementarea reala a obiectui peste RMI. Folosirea acestui mecanism este în mod evident mult mai complexa si numai aplicabila în situatii sigure.

5.2 Metode

Interfata Remote nu declara nici o metoda, servind numai pentru a identifica toate interfetele de la distanta. Toti parametrii si rezultatele unei metode de la distanta trebuie sa fie serializate catre stream-urile de obiect.

5.3 Exceptii

Toate metodele unei interfete Remote trebuie sa declare ca ele pot sa genereze exceptii de tipul RemoteException în plus la niste exceptii pe care ele le pot genera normal. Când un client apeleaza o metoda de la un obiect de la distanta si conexiunea RMI esueaza o exceptie RemoteException va fi trimisa la apelant. Daca implementarea de la distanta primeste oricare alta exceptie aceasta va fi trecuta de asemenea înapoi la apelant, ca si cum apelarea metodei ar fi fost facuta local.

5.4 Exemplu

Interfata BankAccount de mai jos descrie un cont de banca accesibil de la distanta:

import java.rmi.*

public interface BankAccount extends Remote

{

public abstract void deposit (int amount) throws RemoteException

public abstract void withdraw (int amount)

public abstract int getbalance () throws RemoteException

public abstract Portfolio addToPortfolio (Portfolio portfolio) throws RemoteException

T

Pentru ca aceasta interfata sa fie interfata de la distanta, trebuie sa extindem interfata Remote. Declaram patru metode: deposit(), withdraw(), getBalance(), si addToPortfolio(). Acestea sunt singurele metodele pe care un client poate sa apeleze de la distanta pe o implementare BankAccount. Toate patru metode trebuie sa declare ca ele pot sa genereze exceptii de tipul RemoteException. Astfel de exceptii vor aparea daca un client incearca sa apeleze o metoda de la distanta pe o implementare BankAccount si înca multe altele, daca în timpul mecanismului de invocare a metodei de la distanta apar esecuri .

În plus, metoda withdraw() declara ca el poate sa genereze exceptii de tipul InsufficientFoundsException. Daca un client încearca sa retraga prea multi bani de la un cont si serverul genereaza o exceptie de aceast tip, ea va fi în mod automat transferata de catre RMI inapoi la client: RMI-ul sprijina în mod transparent mecanismul de exceptie Java asa cum el sprijina alte semantici ale apelarilor de metoda în Java.

Metoda AddToPortfolio() adauga detaliile acestui BankAccount la un obiect specificat numit Portfolio. Acest este un exemplu trecerei prin valoare a RMI-ului apel^nd semanticele. In mod traditional, într-un cadru nedistribuit, metoda AddToPortfolio() ar modifica direct obiectul Portfolio. Sub RMI, oricum, orice modificare facuta de server este facuta la o copie a obiectului, si astfel, schimbarile nu sunt vazute de client. În schimb, noi trebuie sa returnam obiectul modificat Portfolio la apelant ca resultat al unei apelari de metoda.

6. Clasa Naming

Aceasta clasa care face parte din pachetul java.rmi furnizeaza un API convenabil pentru manipularea numelor registrilor RMI. Intern aceasta clasa foloseste interfata Registry si clasa LocateRegistry; oricum ea furnizeaza metodele statice si notarea convenabila a URL-urilor pentru adresarea obiectelor.

Aceasta clasa furnizeaza metode pentru localizarea obiectelor inregistrate în nume registrului si pentru înregistrarea obiectelor noi. Un client poate sa foloseasca aceasta clasa pentru a localiza un obiect de la distanta si un server poate sa foloseasca aceasta clasa pentru a se înregistra.

6.1 Adresarea serviciilor

Oric^nd este facut local sau este facut inregistrat clasa Naming foloseste un URL ca sintaxa pentru a identifica numele registrului si numele serviciului.

Un URL caracteristic este de forma rmi://hostname:port/service. Acest URL identifica obiectul de la distanta numit service inregistrat în executia numelui registrului pe calculatorul hostname si portul port.

De exemplu, pentru a adresa contul din banca al unei persoane, noi am putea sa folosim urmatorul URL:

rmi://accounts.bigbrother.bank:1099/personal/Romania/Ionut&Petronela

Acest URL se refera la obiectul de la distanta înregistrat sub numele personal/Romania/Ionut&Petronela în executia numele registrului la portul 1099 al accounts.bigbrother.bank. Numele de serviciu este oricare sir arbitrar. Nu exista nici un set de reguli care sa guverneze forma care poate sa o ia numele de serviciu desi putem folosi niste ierarhii pe masura ce noi vrem fim folositori pentru scopurile administrative.

De fapt, toate elementele ale specificatiei URL sunt optionale. Prefixul RMI: poate fi omis daca dorim. Daca hostname nu este specificat, atunci masina locala este folosita. Daca nici un port nu este specificat, numele registrului implicit (1099) este folosit. În cele din urma, numele de serviciu poate fi omis daca listam numai serviciile continute în nume registrului.

6.2 Metode statice

Toate metodele oferite de aceasta clasa sunt statice:

Remote lookup(String address) throws

MalformedURLException, RemoteException,NotBoundException

Aceasta metoda returneaza o referire despre serviciul de la distanta specificat de address, care poate sa fie un URL de forma adreselor de serviciu descrise ca mai sus. Ea se conecteaza la numele registrului specificat si extrage o referire despre obiectul de la distanta înregistrat acolo sub numele de serviciu specificat. Acesta genereaza exceptia NotBoundException daca serviciul nu este înregistrat.

void bind(String address, Remote object) throws

MalformedURLException, RemoteException,

AlreadyBoundException

Aceasta metoda contine o referire despre obiectul specificat de la distanta object la adresa address. Ea se conecteaza la numele serviciului specificat. Acesta genereaza o exceptie AlreadyBoundException daca un obiect este deja continut în numele registrului sub numele de serviciu selectat. Din motive de protectie aceasta metoda poate numai sa fie apelata de un cod care se executa pe acelasi calculator gazda ca si numele registrului; asa ca, partea gazda a adresei address ar trebui sa fie goala.

void rebind(String address, Remote object) throws

MalformedURLException, RemoteException

Aceasta metoda contine o referire despre obiectul de la distanta specificat object la adresa specificata address. Orice obiect deja continut la aceasta adresa este înlocuit. Din motive de securitate aceasta metoda poate numai sa fie apelata de un cod care se executa pe acelasi calculator gazda ca si numele registrului.

void unbind(String address) throws

MalformedURLException, RemoteException,

NotBoundException

Aceasta metoda sterge obiectul de la distanta continut la adresa specificata address. Ea se conecteaza la numele registrului specificat si sterge intrarea continuta acolo sub numele serviciului specificat. Acesta genereaza NotBoundException daca un asemenea serviciu nu este înregistrat. Din motive de securitate, aceasta metoda poate numai sa fie apelata de un cod care se executa pe acelasi calculator gazda ca si numele registrului.

Stringst list(String address) throws MalformedURLException, RemoteException

Aceasta metoda returneaza o lista a tuturor serviciilor de la distanta înregistrate în numele registrului la adresa address. Numai partea gazda a acestei adrese este luata în consideratie; partea numelui de serviciu, în acest caz, este ignorat. Fiecare intrare este returnata ca o adresa plina de la distanta de forma RMI://hostname:/port/service

6.3 Exceptii

Toate metodele clasei Naming pot sa genereze o varietate de exceptii, incluzând:

MalformedURLException

Obiectele de la distanta sunt adresate prin aceasta folosire a clasei folosind URL-urile. Daca un nume furnizat nu urmeaza sintaxa permisa, o exceptie MalformedException va fi generat.

RemoteException

Clasa Naming foloseste intern RMI-ul pentru a accesa numele registrului. Daca o eroare se întâmpla în timpul unei apelari RMI (de exemplu daca apare o eroare de retea), problema va fi anuntata ca RemoteException. Diversele subclasele indica problemele specifice. De exemplu, o exceptie ConnectException va fi generata daca numele registrului nu poate fi contactat (caracteristic aceasta inseamna ca nimeni nu rula pe gazda specificata sau portul specificat).

UnknownHostException

O parte a unor adrese ale unui obiect de la distanta este gazda pe care obiectul este localizat. Daca numele gazdei nu este valabil, o exceptie UnknownHostException va fi generata. Aceasta exceptie este o subclasa a exceptiei RemoteException; Ea este incapsularea ei si a nu se confunda cu java.net.UnknownHostException.

NotBoundException

Încercarea de a accesa un obiect cu numele de botez continut în numele registrului va esua cu exceptia NotBoundException daca obiectul cu acel nume nu a fost inregistrat acolo.

AlreadyBoundException

Încercarea de a lega un obiect cu un nume de botez în numele registrului va esua cu exceptia AlreadyBoundException daca un obiect este deja continut in numele registrului sub numele ales.

6.4 Securitatea

Trebuie sa împiedicam potentialele probleme de securitate care pot sa apara daca unui calculator gazda i-a fost permis sa manipuleze continuturile unui nume de registru; incercarile de a modifica numele registrului (folosind diversele metode de legare) va esua daca apelantul nu ruleaza pe aceeasi masina ca si registrul.

Din aceste motive, când manipulam continutul unui nume de registru ar trebui sa omitem numele registrului calculatorului gazda în URL-urile pe care le furnizam. Daca numele de registru local ruleaza pe portul implicit pur si simplu folosim o adresa a serviciului. Daca el ruleaza pe un port nestandard folosim o adresa de forma: //:port/service

6.5 Exemplu

Daca un client doreste sa listeze serviciile listate într-un nume de registru de la distanta, el poate sa foloseasca metoda list() dupa cum urmeaza:

Strings t Services=Naming.list (“//accounts.my.bank/”);

for (int i = 0;i < services.length ; ++i)

System.out.println (servicessit);

Acesta va lista toate serviciile înregistrate return^nd vectorul services de la adresa urmatoare:

rmi://accounts.my.bank/personal/bob

Elementele vectorului returnat pot fi folosite direct în apelarile suplimentare la clasa Naming. De exemplu:

BankAccount firstAccount = (BankAccount) Naming.lookup (servicess0t);

Aceasta metoda returneaza o referire de la distanta despre primul serviciu returnat de metoda list(). Sa remarcam ca numele registrului poate sa listeze serviciile care ruleaza oriunde asa ca acest obiect de la distanta nu are nevoie sa ruleze pe aceeasi masina ca si numele registrului.

Naming.bind(“First Account”, firstAccount);

În cele din urma, noi inregistram o referire la rezultatul contului de la distanta în numele registrului local sub numele First Account. De obicei vom inregistra local numai obiectele de la distanta daca dorim . Banca poate ea însusi, de exemplu, sa adauge un nou cont dupa cum urmeaza:

account=new PersonalBankAccountImpl (“mag”);

Naming.rebind(“personal/mag”, account);

Aici cream o noua PersonalBankAccountImpl (aceasta ar trebui sa fie un server RMI care pune în aplicare interfata de la distanta BankAccount) si o inregistram în registrul local sub numele de serviciu personal/mag.

7. Clasa LocateRegistry

Aceasta clasa din pachetul java.rmi.registry poate sa fie folosita pentru a controla numele registrilor. Ea furnizeaza metodele pentru a obtine referirile de la distanta despre numele registrilor ca si cum metoda va porni direct numelui registrului.

7.1 Metode statice

Toate metodele ale acestei clase sunt statice. Ele returneaza obiectele care pot sa fie manipulate prin interfata Registry, pe care le vom discuta in continuare.

Registry getRegistry() throws RemoteException

Aceasta metoda returneaza o referire de la distanta despre numele registrului care ruleaza pe masina locala la portul implicit al numelui registrului.Aceasta metoda poate sa genereze o exceptie corespunzatoare numita RemoteException daca apare o eroare RMI. Sa remarcam ca aceasta metoda nu poate încerca sa ia legatura cu numele registrului, si astfel nu vom identifica imediat daca acolo nu se afla un nume de registru care asculta; vom afla numai când încercam de fapt sa accesam registrul.

Registry getRegistry(int port) throws RemoteException

Aceasta metoda returneaza o referire de la distanta la numele registrului care ruleaza pe masina locala la portul specificat port.

Registry getRegistry(String host) throws RemoteException

Aceasta metoda returneaza o referire de la distanta despre numele registrului care ruleaza pe calculatorul gazda specificat host la port implicit al numelui registrului. Aceasta metoda va genera o exceptie numita UnknownHostException daca gazda specificata nu poate sa fie localizata.

Registry getRegistry(String host, int port) throws RemoteException

Aceasta metoda returneaza o referire de la distanta despre numele registrului care ruleaza pe calculatorul gazda specificat numit host la portul specificat numit port.

Registry createRegistry(int port) throws RemoteException

Aceasta metoda creeaza si porneste numele registrului care ruleaza pe masina locala la portul specificat port. Ea returneaza o referire directa la registrul rezultat si nici o referire de la distanta cum sunt returnate de toate celalalte metode. Sa remarcam, prin virtutea stilului în care obiecte de la distanta sunt puse în aplicare ca o aplicatie poate sa creeze numai un singur registru.

Acest registru este un tipic obiect de la distanta asa ca el va fi str^ns ca gunoi si se va termina când acolo nu va mai exista nici o referire despre el, de la distanta sau locala. Referirile de la distanta despre numele registrilor nu persista de obicei pentru mult timp; ele sunt doar mentinute pe durata unei priviri ascendente a unui obiect. Asa ca pentru a pastra executia registrilor, trebuie sa mentinem un server activ cu o referire la acest registru.

Registry createRegistry(int port, RMIServerSocketFactory

servers, RMIClientSocketFactory clients) throw RemoteException

Aceasta metoda creeaza si porneste numele registrului care ruleaza pe masina locala la portul specificat port folosind serverele specificate RMIServerSocketFactory si clientii specificati RMIClientSocketFactory. Numele registrului va asculta pentru conexiunile care vin folosind ServerSocket create de servers si vor instrui clientii de la distanta pentru a se conecta folosind clients.

7.2 Exceptii

Diversele metode ale acestei clase pot sa genereze toate exceptiile de tipul RemoteException daca o eroare RMI este int^lnita când localizam numele registrului. De exemplu, o exceptie de tipul UnknownHostException va fi generata daca un nume de gazda nu poate sa fie localizat.

7.3 Exemplu

Un registru de la distanta foloseste aceasta clasa dupa cum urmeaza:

Registry registry=LocateRegistry.getRegistry (“host”, 1234);

registry.bind(“Service”, service);

Aceasta apelare va returna o referire despre numele registrului specificat care ruleaza la portul 1234 al calculatorului gazda host, care poate fi manipulat prin Naming, cum ar fi interfata Registry. In acest caz vom relega intrarea Service. Daca nici un nume de registru nu ruleaza metoda rebind() va esua cu exceptia RemoteException.

Crearea unui nume de registru local este la fel de simplu:

Registry registry=LocateRegistry.createRegistry (1234);

registry.bind(“Service”, service);

Aici noi cream un registru care ruleaza pe masina locala la portul 1234. Daca acolo se afla deja un registru care ruleaza la acest port, o exceptie corespunzatoare RemoteException va fi generata. Putem altfel sa legam serviciul nostru direct la acest registru.

8. Interfata Registry

Aceasta interfata de la distanta din pachetul java.rmi descrie API-ul numelui registrului RMI. Acest nume de registru este el însusi implementat ca un obiect de la distanta; clasa LocateRegistry astfel pur si simplu returneaza o referire de la distanta despre implementarea JDK a acestei interfete.

8.1 Variabile statice

Interfata Registry are o singura constanta statica:

int REGISTRY_PORT

Acest port este portul implicit la care numele registrului asculta (1099). Daca nu specificam un numar de port când localizam un obiect într-un nume de registru, registrul va fi contactat la acest port.

8.2 Metode

Urmatoarele metode de la distanta sunt definite de interfata Registry. Spre deosebire de clasa Naming când specificam un nume de serviciu prin aceasta interfata, specificam numai numele de serviciu si nu adresa numelui registrului.

Metodele clasei Naming sunt, de fapt, pur si simplu implementate ca apelari la clasa LocateRegistry urmate de apelari de metoda de la distanta prin aceasta interfata.

Remote lookup(String name) throws RemoteException,

NotBoundException

Aceasta metoda returneaza o referire de la distanta despre un serviciu înregistrat sub numele de name în acest nume de registru. Acesta genereaza o exceptie NotBoundException daca serviciul nu este înregistrat.

void bind(String name, Remote object) throws

RemoteException, AlreadyBoundException

Aceasta metoda contine o referire despre obiectul de la distanta specificat object în registru sub numele de name. Obiectul de la distanta object poate sa fie o implementare locala a unui obiect de la distanta sau o referire actuala de la distanta despre un obiect de la distanta. Aceasta genereaza o exceptie AlreadyBoundException daca un obiect este deja continut în numele serviciului sub numele de serviciu selectat. Din motive de securitate aceasta metoda poate numai sa fie apelata de un cod care se executa pe acelasi calculator gazda ca si numele registrului.

void rebind(String name, Remote object) throws

RemoteException

Aceasta metoda contine o referire despre obiectul de la distanta specificat object în acest registru sub numele de name, suprascriind oricare intrare existenta.

void unbind(String name) throws RemoteException,

NotBoundException

Aceasta metoda sterge oricare serviciu care s-a înregistrat sub numele de name de la acest nume de registru. Acesta genereaza NotBoundException daca un asemenea serviciu nu este înregistrat.

strings t list() throws RemoteException

Aceasta metoda returneaza o lista completa a serviciilor inregistrate in acest nume de registru. Numai numele serviciului este returnat; nici o informatie despre adresa nu este inclusa; de exemplu Service1, Service 2.

8.3 Exceptii

Toate metodele interfetei Registry declara ca ei pot sa genereze o varietate de exceptii, comparabile cu acele pentru clasa Naming. De exemplu, o exceptie RemoteException este returnata daca o eroare RMI este întâlnita; o exceptie NotBoundException, daca un serviciu cerut nu este înregistrat si o exceptie AlreadyBoundException, daca un serviciu este deja înregistrat sub un nume propus.

În cele din urma, o exceptie RemoteException de tipul AccessException va fi generata daca o cerere este nepermisa. Toate operatiile actualizate sunt limitate la clientii care se executa pe aceeasi masina gazda ca si numele registrului. Daca un calculator gazda asteapta o astfel de operatie o exceptie AccessException va fi ridicata.

9. Clasa RemoteObject

Clasa RemoteObject din pachetul java.rmi este ultima supraclasa a tuuror obiectelor de la distanta. Aceasta este supraclasa implementarilor obiectului de la distanta si a stub-urilor de la distanta. Clasa RemoteObject suprascrie metodele hashcode(), equals() si toString() pentru a reflecta semanticele corecte ale metodei unui obiect de la distanta. Ea de asemenea implementeaza clasa Serializable pentru a pune în aplicare serializarea corecta obiectelor de la distanta. Ea nu furnizeaza nici o metoda folositoare acestei reimplementari.

9.1 Obiectele de la distanta vazute ca parametri

Este important sa întelegem deosebirea dintre trimiterea unui obiect de la distanta ca un parametru la o apelare de la distanta si serializarea unui obiect de la distanta folosind stream-urile de obiect. Desi RMI-ul foloseste stream-urile de obiect pentru a trimite parametrii la o apelare de metoda de la distanta, el de fapt foloseste subclasa specializata care suprascrie implicit serializarea obiectelor de la distanta.

Când trimitem o implementare a unui obiect de la distanta (o subclasa numita RemoteObject) ca parametru la o apelare de metoda de la distanta, de fapt trimitem o referire de la distanta a implementarii obiectului si nu o copie a lui (în acest caz obiectele normale). Când metoda de la distanta apeleaza ea însusi metodele obiectului pe care le primeste, apelarile RMI vor fi facute înapoi la implementarea originala a obiectului de la distanta.

Acest lucru are o consecinta extrem de importanta. Nu putem sa trimitem nici un obiect de la distanta ca un parametru la o apelare de la distanta si sa ne asteptam ca apelantul va primi o copie a clasei actuale a obiectului de la distanta. În schimb apelantul va primi o copie clasei stub RMI care contine intern o referire de la distanta despre implementarea actuala a obiectului.

public class MyImpl extends UnicastRemoteObject implements

my Remote

{

// …

public static void main (Strings t args) throws Exception

{

MyImpl mine = new MyImpl();

HisRemote his = (HisRemote) Naming.lookup

(“//server/his”);

his.invoke(mine);

T

T

În secventa de mai sus, cream un nou obiect local de la distanta numit mine si îi trimitem ca parametru un alt obiect de la distanta numit this. Dupa transferul de date RMI, metoda invoke() nu va primi un obiect actual MyImpl. El primeste un obiect MyImpl_Stub care implementeaza interfata MyRemote si cele mai apropriate metode definite în aceasta privinta, inapoi la exempluul nostru de MyImpl.

De aceea este o eroare sa definim o metoda de la distanta care primeste ca parametru o implementare actuala a clasei obiectului de la distanta. Trebuie sa definim numai metodele de la distanta pe care le primeste, pe masura ce parametrii sunt ori obiecte locale sau interfete de la distanta. Urmatoarele sunt niste declaratii posibile ale metodei invoke():

public void invoke (Object object) throws RemoteException

Aceasta sintaxa a metodei este corecta; clasa MyImpl_Stub care va fi trimisa este o subclasa a clasei Object.

public void invoke (Myremote Remote) throws RemoteException

Aceasta sintaxa a metodei este corecta; clasa MyImpl_Stub implementeaza clasa MyRemote, si astfel, ea este un parametru corect al acestei metode.

public void invoke (MyImpl Impl) throws RemoteException

Aceasta sintaxa a metodei este invalida. La compilare, totul arata bine; trecem într-o copie a clasei MyImpl. Oricum, cadrul RMI va transforma o copie a clasei MyImpl într-o copie a clasei MyImpl_Stub în timpul serializarii. Aceasta clasa a stub-ului nu este o subclasa a clasei MyImpl, si astfel, la executie, va rezulta o exceptie interna RMI.

9.2 Serializarea unui obiect de la distanta

Când serializam un obiect de la distanta folosind stream-urile normale ale obiectelor, un obiect complet valid de la distanta va fi serializat. Când deserializam pe acesta obiectul este în mod automat reanimat. Folosind acest mecanism putem serializa un obiect de la distanta pe un disc, si dupa aceea sa il deserializam, ori în acelasi JVM ori în alt JVM aflat în alta parte din lume si astfel un complet obiect valid de la distanta va fi creat si expus. Este chiar posibil pentru clasele noastre de la distanta sa implementam o metoda ReadObject() care în mod automat înregistreaza obiectul de la distanta într-un nume de registru.

// ObjectOutputStream ObjectOut;

MyImpl mine = new MyImpl();

ObjectOut.writeObject(mine);

Exemplul de mai sus arata ca MyImpl este serializat asa cum ne asteptam folosind procesul normal de serializare.

// ObjectInputStream ObjectIn;

MyImpl mine2 = (MyImpl) objectIn.readObject();

Acum avem un duplicat al obiectului original de la distanta. Acesta este un obiect complet activat de la distanta care poate sa fie înregistrat într-un nume de registru si manipulat exact ca si copia originala.

10. Clasa RemoteServer

Aceasta clasa, care este o subclasa a clasei RemoteObject care furnizeaza mai multe semantici specializate pentru implementarile obiectelor de la distanta. Ea furnizeaza functiile comune ale obiectelor de la distanta care vor fi implementate ca servere; care sunt, obiectele de la distanta care vor deschide socket-urile serverului pentru a accepta apelarile clientului.

10.1 Metode statice

Clasa RemoteServer furnizeaza câteva metode statice folositoare:

String getClientHost() throws ServerNotActiveException

Aceasta metoda, când este apelata dintr-o implementare a unei metode de la distanta returneaza adresa masinei clientului. Daca apelarea actuala de la distanta este facuta din afara, ea genereaza o exceptie ServerNotActiveException. Aceasta metoda este extrem de folositoare pentru controlul accesului: Într-o implementare accesibila de la distanta a unei banci putem sa apelam metoda getClientHost() pentru a determina calculatorul gazda care încearca sa apeleze o metoda de la distanta, si putem sa înregistram accesul în consecinta. Sa remarcam ca DNS-ul si IP-ul nu sunt sigure asa ca un nume gazda nu este într-adevar o indicatie puternica de identificare, dar este un inceput.

void setLog(OutputStream out)

RMI-ul furnizeaza facilitate convenabila de conectare(login) care ne permite sa ne conectam în întregime la apelarile RMI care vin. Apelam aceasta metoda static cu un stream rezultat corespunzator pentru a activa conectarea. Pun^nd null vom dezactiva conectarea.

RemoteServer.setlog(new FileOutputStream(“rmi.log”));

Aceasta apelare începe sa conecteze toate apelarile RMI la acest JVM;

conectarea este scrisa la clasa specificata.

OutputStream.RemoteServer.setLog(null);

Aceasta apelare schimba conectarea RMI(off). Sa remarcam ca aceasta nu inchide stream-ul original; putem facem acest lucru manual ori sa asteptam ca colectia de gunoi sa aiba grija sa inchida acest stream in locul nostru.

PrintStream getLog()

Aceasta metoda returneaza stream-ul conectarii RMI. Tot ce scriem despre acest stream este completat cu informatie aditionala de conectare incluzând timpul actual si expediat la stream-ul curent al conectarii.

11. Clasa UnicastRemoteObject

Clasa UnicastRemoteObject, o subclasa a clasei RemoteServer, este supraclasa pentru cele mai multe obiecte normale de la distanta. Cu exceptia cazurilor neobisnuite si complexe sau când folosim mecanismul de declansare RMI vom implementa întotdeauna obiectele de la distanta ca o extensie a acestei clase.

Clasa UnicastRemoteObject adauga detaliile finale de implementare în legatura cu obiectele necopiate accesibile de la distanta folosind stream-urile TCP. Printre alte detalii ea adauga sprijin pentru metoda clone() pentru ca sa putem clona un obiect de la distanta pentru a obtine un alt, unic, obiect de la distanta. Aceasta clasa adauga de asemenea ultima scena care sprijina serializarea astfel încât un obiect de la distanta serializat ram^ne un obiect de la distanta.

11.1 Constructori

Urmatorii constructori sunt furnizatI de aceasta clasa:

protected UnicastRemoteObject() throw RemoteException

Acesta este un costructor standard pe care o implementare a obiectului de la distanta trebuie sa il apeleze daca constructorul este implementat de o subclasa a clasei UnicastRemoteObject. El initializeaza obiectul pentru a accepta apelari de metoda de la distanta la un port arbitrar liber local. Daca o eroare este întâlnita c^nd se executa aceasta initializare, o exceptie corespunzatoare RemoteException va fi generata.Sa remarcam ca executia RMI poate sa aleaga sa serveasca obiecte multiple de la distanta de la o singura clasa ServerSocket pentru a salva o cantitate a firului de executie si resurse de retea pe care el trebuie sa le consume la numerele de gazda mari ale obiectelor.

protected UnicastRemoteObject(int port) throw RemoteException

Acest constructor ar trebui sa fie apelat de o implemetare a unui obiect de la distanta care doreste sa asculte pe portul local specificat port. De exemplu numele registrului RMI foloseste constructorul pentru a porni portul lui ales.

protected UnicastRemoteObject(int port,

RMIClientSocketFactory clients, RMIServerSocketFactory servers) throw RemoteException

Acest constructor ar trebui sa fie apelat de o implementare a unui obiect de la distanta care doreste sa foloseasca de obicei fabrice de socket-uri pentru scopurile de comunicare. El va asculta conexiunile care vin la portul specificat port (folosim valoarea 0 pentru a asculta la un port arbitrar) folosind clasa ServerSocket creata de servers. Clientii de la distanta ale acestei clase vor fi instruiti sa foloseasca clients pentru a stabili conexiunile Socket la acest server.

11.2 Metode statice

În plus pentru a fi supraclasa pentru cel mai distantat obiect object, aceasta clasa furnizeaza o extrem de importanta metoda de ajutor:

RemoteStub exportObject(Remote Object) throws

RemoteException

Aceasta metoda exporta obiectul specificat object ca un obiect de la distanta ascultând la un port arbitrar liber. Valoarea returnata este, în efect, o referire de la distanta despre obiect care poate sa fie continut într-un nume de registru sau prelucrat asa cum noi dorim. Aceasta metoda este importanta pentru ca inseamna ca putem sa implementam un obiect de la distanta fara o subclasa directa a clasei UnicastRemoteObject. În c^teva cazuri, ca de exemplu în applet-uri sau unde nu putem sa controlam un cod, nu este practic sa nu folosim o subclasa directa a clasei UnicastRemoteObject. În acest caz ar trebui sa cream clasa noastra necesara implement^nd interfata noastra de la distanta ca de obicei; atunci putem sa exportam o copie a clasei noastre impreuna cu aceasta metoda si sa inregistram stub-ul rezultat intr-un nume de registru.

import java.rmi.*;

import java.rmi.server.*;

public class MyClass extends SomeClass implements

MyRemote

{

// methods

public static void main (Strings t args) throws

RemoteException

{

MyClass obj = new MyClass();

MyRemote ref = (MyRemote)

UnicastRemoteObject.exportObject (obj);

Namimg.rebind (“MyClass/MyInstance”, ref);

T

T

Acest fragment de cod, dupa compilarea cu rmic, furnizeaza exact aceeasi functie ca si cum noi am subclasa direct clasa UnicastRemoteObject si ne-am înregistrat în numele registrului în locul acestei referiri. Sa remarcam, oricum, ca în acest caz noi putem sa extindem oricare clasa care o dorim. Mai ales, acest lucru poate fi folositor pentru applet pentru a se expune el însusi ca un obiect de la distanta accesibil.

RemoteStub exportObject(Remote object, int port) throws

RemoteException

Aceasta metoda exporta obiectul specificat Object ca un obiect de la distanta ascultând la portul specificat port.

RemoteStub exportObject(Remote object, int port,

RMIClientSocketFactory clients, RMIServerSocketFactory servers) throws RemoteException

Aceasta metoda exporta obiectul specificat object ca obiect de la distanta ascult^nd la portul specificat port folosind fabricele specificate de socket, clients si servers pentru scopurile comunicarii (partea 16).

boolean unexportObject(Remote object, boolean force)

throws NoSuchException

Aceasta metoda nu exporta obiectul specificat de la distanta object (o subclasa a clasei UnicastRemoteObject sau un obiect exportat de exportObject()), return^nd true(adevarat) daca neexportul reuseste. Daca obiectul este curent în folosinta atunci aceasta metoda va esua cu exceptia cazului in care force este true, în acest caz obiectul este fortat abandonat. Dupa neexportare, obiectul nu va mai fi disponibil pentru a accepta apelari de metoda de la distanta.

12. Clasa RemoteStub

Clasa RemoteStub este supraclasa tuturor claselor de la distanta ale stub-ului. Acestea sunt clasele care apeleaza cel mai aproape de la distanta de la un JVM la altul. Când rulam comanda rmic, fisierul stub care este creat este o subclasa a clasei RemoteStub. El implementeaza aceleasi interfete de la distanta ca si clasa originala de la distanta, dar aproprie toate metodele de la distanta în apelari de metoda de la distanta pe implementarea actuala a obiectului de la distanta (prin conectare si comunicare cu skeleton-ul). C^nd cautam o referire de la distanta într-un nume de registru, de fapt primim o copie a clasei acestui stub.

Singura, clasa RemoteStub nu furnizeaza nici o metoda de folosire particulara.

13. Interfata Unreferenced

Interfata Unreferenced este folositoare pentru obiectele de la distanta care doresc sa fie înstiintate oricând daca nu exista în plus referiri de la distanta despre ele.

Pentru a implementa colectia de gunoi de la distanta, cadrul RMI mentine în mod automat o numaratoare a numarului de referiri care exista pentru fiecare obiect de la distanta. Interfata Unreferenced este o cale de mijloc pentru codul utilizatorului care interactioneaza cu acest mecanism de colectie de gunoi.

13.1 Metode

Numai o metoda este definita:

void unreferenced()

Aceasta metoda va fi apelata de un obiect de la distanta care implementeaza interfata Unreferenced descrisa mai sus, oricând numararea referirilor de la distanta descreste la zero. Sa remarcam ca aceasta numaratoare a referirilor este independenta de numaratoarea referirilor locala; at^ta timp, c^t exista înca referiri locale la obiectul local. Acest mecanism este de obicei folosit pentru dezactivarea manuala a obiectelor locale atunci când ei nu mai au folosiri de la distanta.

Pentru a folosi acest mecanism de callback, obiectul nostru de la distanta ar trebui sa implementeze simplu interfata Unreferenced si metoda unreferenced(). Atunci el va fi în mod automat înstiintat atunci când nu se mai exista nici un interes de la distanta. Sa remarcam ca o intrare într-un nume de registru este pur si simplu o referire de la distanta despre obiectul înregistrat, asa ca atâta timp cât obiectul nostru este continut intr-un nume de registru, el nu va fi nereferit.

Colectia de gunoi distribuita se bazeaza pe referirile periodice de la distanta înstiintând un obiect de la distanta ca ele sunt înca active. Perioada pentru aceasta înstiintare este destul de mare (implicit, 10 minute) asa ca nu ar trebui sa ne bazam pe existenta înstiintarii punctuale a unor referiri de la distanta.

14. Clasa RMISocketFactory

Aceasta clasa din pachetul java.rmi.server este o fabrica pentru crearea de diverse conexiuni de retea care sunt necesare în timpul functionarilor RMI. Aceasta clasa este importanta pentru ca ea ne permite sa instalam de obicei socket-ul manipulând facilitatile pentru aplicatiile noastre RMI. Oricum, pentru cele mai multe situatii, cu exceptia cazurilor c^nd protectia este necesara, fabrica implicta de socket-uri este suficient de puternica; ea în mod transparent încearca trei diferite tipuri de conexiunea de retea. In cazul in care o protectie de date este necesara este folosita o alta fabrica personalizata descrisa putin mai jos.

14.1 Cele mai apropriate date RMI

Când cadrul RMI doreste sa se conecteze la un obiect de la distanta, el mai întâi încearca sa creeze o conexiune directa TCP-Socket la obiect. Din spatele unui firewall (zid de protectie), aceasta conexiune poate sa fie nepermisa.

Daca aceasta conexiune esueaza o conexiune HTTP este încercata direct la portul la care obiectul de la distanta asculta Obiectele de la distanta ofera suport pentru transferul de date direct si pentru transferul de pachete de date prin HTTP. Astfel, cadrul RMI se va conecta la serverul local de Web, daca este configurat, si il va întreba de cea mai apropriata cerere HTTP directa la obiectul de la distanta. El va face cea mai aproapriata cerere care este:

POST http://server:rmiObjectPort/data…

Aceasta cerere va merge de la cadrul RMI la serverul local Web, care ar trebui sa se conecteze direct la obiectul de la distanta.

În cele din urma, daca corporatia firewall sau serverul Web permit circulatia la portul 80 a site-urilor departate, o conexiune va fi încercata la un program CGI (Common Gateway Interface, interfata comuna pentru porti de acces) pe serverul de la distanta Web care învârteste cea mai apropriata cererea direct la obiectul de la distanta. În acest caz cadrul RMI va încerca urmatoarea cerere:

POST http://server:80/cgi-bin/java-rmi.cgi/data…

Aceasta cerere va merge direct la serverul de la distanta Web daca firewall permite circulatia la portul 80 sau va merge de la cadrul RMI la serverul local Web,la serverul de la distanta Web, la programul RMI CGI, si de acolo la obiectul actual de la distanta. Acest program CGI cel mai aproape este inclus în directorul bin al distribuirii standard JDK.

14.2 Metode statice

Metodele urmatoare sunt furnizate pentru a instala serviciile socket globale RMI:

void setSocketFactory(RMISocketFactory factory) throw

IOException

Aceasta metoda instaleaza o noua fabrica de socket. Aceasta clasa va fi raspunzatoare pentru crearea tuturor socket-urilor viitoare RMI în acest JVM. Daca nici o fabrica nu este instalata, implementarea implicita este folosita.

Putem sa instalam numai o data fabrica de socket, asa ca aceasta metoda va ridica exceptia IOException daca o fabrica este deja definita. Aceasta metoda este de asemenea controlata de actualul SecurityManager; codul nu poate sa înregistreze o fabrica de socket (o exceptie SecurityException va fi ridicata).

RMISocketFactory getSocketFactory()

Aceasta metoda returneaza de obicei fabrica curenta de socket, daca exista, daca nu returneaza null.

RMISocketFactory getDefaultSocketFactory()

Aceasta metoda returneaza fabrica de socket implicita RMI.

void setFailureHandler(RMIFailureHandler failureHandler)

Aceasta metoda instaleaza un handler(numar) asociat esecului RMI. Esecul este apelat daca acolo exista o problema la crearea socket-ului RMI; Ea are atunci prilejul sa rezolve problema înaintea înainte RMI-ul sa incerce iar.

RMIFailureHandler getFailureHandler()

Aceasta metoda returneaza handler-ul actual asociat esecului, în cazul ca exista iar valoarea null daca nu exista.

14.3 Metode

Aceste metode trebuie sa fie implementate de o fabrica RMI de socket-uri. O copie a oricarei clasei ar trebui sa fie înregistrata prin metoda setSocketFactory().

abstract Socket createSocket(String host, int port) throws

IOException

Aceasta metoda ar trebui sa returneze un nou Socket conectat la gazda specificata host si la portul port. Orice exceptie care apare ii poate fi trecuta sau daca dorim sa sprijinim alte încercari de conexiune diferite poate fi prelucrata intern.

abstract ServerSocket createServerSocket(int port) throws

IOException

Aceasta metoda ar trebui sa returneze un nou ServerSocket care asculta la portul specificat port. Exceptiile pot sa fie prelucrate ca mai înainte.

14.4 Interfata RMIFailureHandler

Aceasta interfata descrie un handler asociat esecului RMI. Acesta este o clasa care încearca sa rezolve cauza unei exceptii de socket RMI si sa o corecteze. Daca rezolvarea este terminata cu succes, cadrul RMI poate reîncerca conexiunea initiala.

14.4.1 Metode

Numai o metoda este declarata:

boolean failure(Exception ex)

Aceasta metoda este apelata impreuna cu exceptia care a aparut c^nd am incercat sa cream socket-ul RMI. O implementare a acestei metode ar trebui sa încerce sa adreseze problema si sa returneze valoarea true daca RMI-ul reîncearca operatia de socket originala.

Opurtunitatea de a încerca în întregime reparea problemei depinde de cadrul RMI. El poate, de exemplu, sa se conecteze la server printr-un alt mecanism si sa reînvie un serviciu RMI mort daca aceasta este problema.

14.5 Interfata RMIClientSocketFactory

Aceasta interfata descrie o parte din fabrica de client; aceasta este folosita pentru implementa fabrica personalizata de socket prin obiect.

14.5.1 Fabricile personalizate de socket-uri

Folosind constructorii JDK 1.2 si metodele claselor UnicastRemoteObject si LocateRegistry, obiectele de la distanta pot sa fie create folosind fabricile personalizate de socket prin obiect pentru scopurile comunicarii. O personalizare caracteristica va fi folosirea protocolului SSL sau un protocol asemanator pentru a cripta si autentifica transferul de date RMI.

Aceste fabrici personalizate de socket prin obiect sunt de obicei preferabile clasei globale RmiSocketFactory pentru ca ele permit un amestec omogenic de fabrici de socket în cadrul unei aplicatii si a unui nume de registru. Acesta compara favorabil fabricile cu o fabrica globala de socket unde comunicarea printre implementarile fabricilor de socket-uri diferite este extrem de dificila. Fabricele personalizate de socket prin obiect au întotdeauna precedenta peste o fabrica de socket globala, daca una este instalata.

Când o referire de la distanta despre un obiect este trecuta dincolo de o apelare RMI, de exemplu, când un server se înregistreaza într-un nume registrului sau când un client obtine o referire de la distanta de la numele registrului, atunci copia obiectului personalizat RmiClientSocketFactory este inclusa în datele referii de la distanta. Când apelantul referirii de la distanta invoca ulterior o metoda pe obiectul de la distanta, el va folosi aceasta fabrica de socket pentru a stabili o conexiune de retea peste care datele RMI vor fi transferate. Obiectul de la distanta va folosi el insusi un obiect personlizat RMIServerSocketFactory pentru a accepta conexiuni care vin; când cele doua fabrici personalizate stabilesc o conexiune ele pot sa execute o autentica handshake (strângere de mâna) sau sa implementeze un protocol de securitate pentru a asigura transferul de date RMI.

Sa remarcam obiectul personalizat RMIClientSocketFactory va fi serializat dincolo de o conexiune RMI când o referire de la distanta este transferata. El trebuie sa fie serializat astfel, ar trebui sa se supuna regulilor de serializare si ar trebui sa foloseasca capabilitatea personalizata a stream-ului de obiect pentru a executa oricare instalare necesara deserializarii.

14.5.2 Metode

Numai o singura metoda este declarata:

Socket createSocket(String host, int port) throw IOException

Aceasta metoda ar trebui sa returneze un nou Socket conectat la gazda specificata host si la portul port. Caracteristic, o fabrica personalizata de socket vrea sa execute handshaking initial sau ea ea va returna o subclasa a clasei Socket care implementeaza, de exemplu, securitatea SSL.

14.6 Interfata RMIServerSocketFactory

Aceasta interfata descrie o parte fabrica server de socket; aceasta clasa este folosita pentru a implementa fabrica personalizata de socket prin obiect.

14.6.1 Metode

Numai o singura metoda este declarata:

ServerSocket createServer(int port) throw IOException

Aceasta metoda ar trebui sa returneze un nou ServerSocket care asculta la portul specificat port, care poate fi 0 pentru un port anonim. Caracteristic, o fabrica personalizata de socket va returna o subclasa ServerSocket care ori executa handshaking initial dincolo Sockets care o accepta ori foloseste o subclasa Socket care implementeaza, de exemplu, protectia SSL.

14.7 Declansarea obiectelor RMI

JDK 1.2 a prezentat o noua capacitate a RMI-ului numita object activation. Folosind aceasta capacitate, implementarile obiectului de la distanta nu au nevoie sa fie active permanent pe server. În schimb, o activation daemon(rmid) se executa permanent pe server. Implementarile obiectului de la distanta sunt înregistrate cu acest daemon în loc sa fie manual executate. Când un client de la distanta invoca o metoda pe un obiect inactiv, demonul va activa în mod automat obiectul astfel ca el sa serveasca apelarile de la distanta. Ulterior, demonul poate sa dezactiveze obiectul pentru a elibera resursele pe care le foloseste.

Sprijinul pentru declansare este furnizat de pachetul java.rmi. activation. Un interes particular este clasa Activatable care imita functia pentru activarea obiectelor a clasei UnicastRemoteObject.

15. Explicatii despre proiect

Pentru a exemplifica notiunile prezentate in capitolele anterioare prezentam in continuare o aplicatie in limbajul Java.

15.1 Tema proiectului

Partea practica a acestei lucrari o reprezinta un test numit Test Navigare Internet deoarece cuprinde intrebari din acest domeniu. Testul este format din 10 intrebari aleatoare generate de un set de intrebari format din 42 de intrebari. Testul doreste sa realizeze o evaluare reala a studentului care sustine acest examen. Timpul de raspuns pentru fiecare intrebare este de 50 de secunde.

15.2 Implementarea proiectului

Acest test demonstreaza invocarea de metoda de la distanta de la un server care permite clientilor sa sustina un test folosind apelarile de metoda de la distanta.

Pentru a crea aplicatia server, trebuie sa executam un numar de pasi.

15.2.1 Definirea interfetei

Mai int^i trebuie sa definim o interfata pentru obiectul de la distanta definind toate metodele care ar trebui sa fie publice. Aceasta interfata trebuie sa extinda java.rmi.Remote, o clasa care pur si simplu identifica de la distanta interfetele accesibile.

Interfata arata astfel:

import java.rmi.Remote;

import java.rmi.RemoteException;

public interface interfata extends Remote

{

public Strings t getIntrebare(int nr) throws RemoteException;

public boolean verificare(String nume, String prenume, String

parola) throws RemoteException;

T

Aceasta interfata defineste doua metode accesibile de la distanta, getIntrebare(int nr) si verificare(String nume, String prenume, String parola) Toate acele metode care sunt declarate într-o interfata de la distanta trebuie sa fie declarate astfel inc^t ele sa genereze toate exceptiile de tipul java.rmi.RemoteException. O metoda poate, desigur, sa fie declarata ca ea sa poata sa genereze alte exceptii, depinz^nd de propria ei alegere.

Metoda getIntrebare(int nr) primeste ca parametru un numar si returneaza intrebarea cu acest numar. Metoda verificare(String nume, String prenume, String parola) primeste ca parametru numele, prenumele si parola studentului care sustine testul si returneaza true daca studentul este inregistrat in fisierul parola.txt si false in caz contrar. Dorim astfel sa facem o verificare a celor care dau testul. De aceea studentul care sustine testul trebuie sa fie inregistrat in fisier sI trebuie sa stie parola. Fisierul parola.txt este de forma:

Magureanu // numele de familie

Ionut // prenumele

tnt // parola

15.2.2 Implementarea interfetei

Implementarea obiectului de la distanta trebuie sa extinda java.rmi.server.RemoteObject sau o subclasa si trebuie sa puna în aplicare fiecare din interfetele de la distanta pe care obiectul doreste sa le suporte. În realitate, java.rmi.server.Unicast.RemoteObject este cea mai acceptabila pentru a fi folosita ca supraclasa; aceasta clasa furnizeaza o implementare a comportarii tuturor obiectelor caracteristice de la distanta.

O implementare a interfetei de mai sus arata astfel:

import java.rmi.registry.LocateRegistry;

import java.rmi.*;

import java.rmi.server.*;

import java.io.*;

public class myServer extends UnicastRemoteObject implements interfata

{

public myServer() throws RemoteException {T;

public boolean verificare(String nume, String prenume, String parola)

{

BufferedReader fisier;

try

{

fisier = new BufferedReader(new FileReader("parola.txt"));

String numeCitit, prenumeCitit, parolaCitita;

while ((numeCitit = fisier.readLine()) != null)

{

prenumeCitit = fisier.readLine();

parolaCitita = fisier.readLine();

System.out.println("Compar: " + nume + " cu " + numeCitit);

System.out.println("Compar: " + prenume + " cu " + prenumeCitit);

System.out.println("Compar: " + parola + " cu " + parolaCitita);

if (nume.equals(numeCitit) && prenume.equals(prenumeCitit) && parola.equals(parolaCitita))

{

fisier.close();

return true;

T

T

fisier.close();

T

catch (IOException e)

{

System.out.println("Eroare: " + e.toString());

T

return false;

T

public Stringst getIntrebare(int numar)

{

Stringst x = new Strings5t;

switch(numar)

{

case 0:

xs0t = "Ce inseamna LAN?";

xs1t = "a) Low Approach Network";

xs2t = "b) Local Area Network (retea locala)";

xs3t = "c) protocol din IP";

xs4t = "b";

break;

case 1:

xs0t = "Ce inseamna LAN?";

xs1t = "a) Low Approach Network";

xs2t = "b) Local Area Network (retea locala)";

xs3t = "c) protocol din IP";

xs4t = "b";

break;

case 2:

xs0t = "Ce inseamna WAN?";

xs1t = "a) protocol din UDP";

xs2t = "b) Weigth Approach Network";

xs3t = "c) Wide Area Network (retea extinsa)";

xs4t = "c";

break;

case 3:

xs0t = "Ce inseamna Internet?";

xs1t = "a) grup de calculatoare (mari, mici) conectate intre ele ";

xs2t = "b) un calculator ce ofera serviciul de Web";

xs3t = "c) reteaua din facultatea noastra";

xs4t = "a";

break;

case 4:

xs0t = "Ce inseamna World Wide Web?";

xs1t = "a) browser";

xs2t = "b) unul dintre cele mai importante servicii Internet";

xs3t = "c) unul dintre cele mai importante protocoale de retea";

xs4t = "b";

break;

case 5:

xs0t = "Ce inseamna navigator Web?";

xs1t = "a) Program client care aduce informatia stocata pe un server Web";

xs2t = "b) Program care navigheaza prin directoarele discului C:";

xs3t = "c) un protocol de retea";

xs4t = "a";

break;

case 6:

xs0t = "Ce inseamna hipermedia?";

xs1t = "a) mediu puternic pentru calculatoare";

xs2t = "b) posta electronica";

xs3t = "c) obiecte/documente de imagini, sunete care includ hiperlegaturi";

xs4t = "c";

break;

case 7:

xs0t = "Ce inseamna ISP?";

xs1t = "a) Internet Service Provider (furnizor de servicii Internet)";

xs2t = "b) facultatea noastra";

xs3t = "c) browser Web";

xs4t = "a";

break;

case 8:

xs0t = "Care dintre urmatoarele sunt caracteristici ale browserelor Web:";

xs1t = "a) Stabilirea de repere pentru site-uri Web favorite";

xs2t = "b) mai multe ferestre de navigare";

xs3t = "c) cadre sau vederi multiple in interiorul unei ferestre";

xs4t = "abc";

break;

case 9:

xs0t = "Care dintre urmatoarele sunt servicii Internet:";

xs1t = "a) FTP";

xs2t = "b) ps";

xs3t = "c) Telnet";

xs4t = "ac";

break;

case 10:

xs0t = "Care dintre urmatoarele sunt caracteristici pentru programe e-mail:";

xs1t = "a) agenda de adrese";

xs2t = "b) cutii de primiri si trimiteri";

xs3t = "c) transmiterea ulterioara a mesajelor";

xs4t = "abc";

break;

case 11:

xs0t = "Ce inseamna site Intranet?";

xs1t = "a) un site ce conecteaza doua retele Internet";

xs2t = "b) un site Web intern";

xs3t = "c) site Internet";

xs4t = "b";

break;

case 12:

xs0t = "Care sunt caracteristicile de diferentiere a ISP-uului:";

xs1t = "a) pret";

xs2t = "b) procentaj vandut din capacitate";

xs3t = "c) asistenta tehnica pentru clienti";

xs4t= "abc";

break;

case 13:

xs0t = "Care sunt taxele impuse de furnizorii de servicii Internet:";

xs1t = "a) per hit";

xs2t = "b) pe megaoctet stocat";

xs3t = "c) pe latime de banda utilizata";

xs4t = "abc";

break;

case 14:

xs0t = "Ce inseamna TCP/IP?";

xs1t = "a) Theoretical Computer Protocol/Intranet Protocol";

xs2t = "b) un subserviciu pentru e-mail";

xs3t = "c) Transmission Control Protocol/Internet Protocol";

xs4t = "c";

break;

case 15:

xs0t = "Care sunt nivelele TCP/IP:";

xs1t = "a) Internet";

xs2t = "b) transport";

xs3t = "c) aplicatie";

xs4t= "abc";

break;

case 16:

xs0t = "Ce inseamna retea Ethernet?";

xs1t = "a) reteaua ce foloseste mediu de transmisie eterul";

xs2t = "b) retea configurata in topologie cu fir coaxial";

xs3t = "c) retea configurata in topologia inel";

xs4t = "b";

break;

case 17:

xs0t = "Care din urmatoarele sunt protocoale din nivelul Internet:";

xs1t = "a) Internet Protocol";

xs2t = "b) Internet Control Message Protocol";

xs3t = "c) Address Resolution Protocol";

xs4t = "abc";

break;

case 18:

xs0t = "Care din urmatoarele sunt protocoale din nivelul de transport:";

xs1t = "a) Transmission Control Protocol";

xs2t = "b) User Datagram Protocol";

xs3t = "c) Dynamic Host Configuration Protocol";

xs4t = "ab";

break;

case 19:

xs0t = "Care din urmatoarele fac parte din nivelul aplicatie:";

xs1t = "a) Ping";

xs2t = "b) Telnet";

xs3t = "c) Rlogin";

xs4t = "abc";

break;

case 20:

xs0t = "Care din urmatoarele fac parte din nivelul aplicatie:";

xs1t = "a) Rsh";

xs2t = "b) FTP";

xs3t = "c) SMTP";

xs4t = "abc";

break;

case 21:

xs0t = "Care din urmatoarele fac parte din nivelul aplicatie:";

xs1t = "a) HTTP";

xs2t = "b) DNS";

xs3t = "c) HTML";

xs4t = "ab";

break;

case 22:

xs0t = "Cum memoreaza IP drumul parcurs de datagrame?";

xs1t = "a) memoreaza nodurile precedente";

xs2t = "b) memoreaza tot traseul pe baza unui circuit virtual";

xs3t = "c) nu memoreaza drumul parcurs de datagrame";

xs4t = "c";

break;

case 23:

xs0t = "Care din urmatoarele campuri exista intr-o datagrama:";

xs1t = "a) lungimea antetului";

xs2t = "b) timpul de viata";

xs3t = "c) adresa expeditorului";

xs4t = "abc";

break;

case 24:

xs0t = "Care dintre urmatoarele adrese internet fac parte din clasa C?";

xs1t = "a) 193.231.30.197";

xs2t = "b) 250.121.15.213";

xs3t = "c) 127.0.0.1";

xs4t = "a";

break;

case 25:

xs0t = "Care dintre urmatoarele campuri fac parte dintr-un segment TCP:";

xs1t = "a) adresa portului sursa";

xs2t = "b) adresa portului destinatie";

xs3t = "c) timpul de viata";

xs4t = "ab";

break;

case 26:

xs0t = "Ce inseamna document static?";

xs1t = "a) care nu se poate copia";

xs2t = "b) cu continut fix, create si memorate pe server";

xs3t = "c) care poate contine variabile si functii statice";

xs4t = "b";

break;

case 27:

xs0t = "Ce inseamna document dinamic?";

xs1t = "a) care nu contine variabile si functii statice";

xs2t = "b) care nu se pot copia";

xs3t = "c) creat de serverul Web cand browserul cere un document";

xs4t = "c";

break;

case 28:

xs0t = "Ce inseamna document activ?";

xs1t = "a) document rezultat in urma apelarii unui program client";

xs2t = "b) document rezultat in urma apelarii unui program server";

xs3t = "c) document care se executa permanent";

xs4t = "a";

break;

case 29:

xs0t = "Ce inseamna MIME?";

xs1t = "a) protocol Internet";

xs2t = "b) specificatie tehnica utilizata pentru transferul de date mutimedia";

xs3t = "c) Multiple Instructions Multiple Exceptions";

xs4t = "b";

break;

case 30:

xs0t = "Care din urmatoarele sunt tranzactii HTTP:";

xs1t = "a) expedierea unui mesaj e-mail";

xs2t = "b) stabilirea conexiunii, trimiterea unei cereri de la client";

xs3t = "c) emiterea unui raspuns de la server, inchiderea conexiunii";

xs4t = "bc";

break;

case 31:

xs0t = "Care dintre urmatoarele sunt metode de cerere complete:";

xs1t = "a) GET";

xs2t = "b) HEAD";

xs3t = "c) POST";

xs4t = "";

break;

case 32:

xs0t = "Care dintre urmatoarele etichete HTML sunt valide:";

xs1t = "a) A";

xs2t = "b) B";

xs3t = "c) I";

xs4t = "abc";

break;

case 33:

xs0t = "Care dintre urmatoarele etichete HTML sunt valide:";

xs1t = "a) DD";

xs2t = "b) DL";

xs3t = "c) DT";

xs4t = "abc";

break;

case 34:

xs0t = "Care dintre urmatoarele atribute HTML sunt valide:";

xs1t = "a) ALIGN";

xs2t = "b) ALT";

xs3t = "c) SRC";

xs4t = "abc";

break;

case 35:

xs0t = "Care dintre urmatoarele etichete HTML sunt valide:";

xs1t = "a) ISINDEX";

xs2t = "b) HREF";

xs3t = "c) MENU";

xs4t = "abc";

break;

case 36:

xs0t = "Care este eticheta HTML pentru liste ordonate?";

xs1t = "a) LIST";

xs2t = "b) OL";

xs3t = "c) UL";

xs4t = "b";

break;

case 37:

xs0t = "Care este eticheta HTML pentru fontul cel mai mare?";

xs1t = "a) H6";

xs2t = "b) BIGGEST";

xs3t = "c) H1";

xs4t = "c";

break;

case 38:

xs0t = "Care este elementul HTML pentru declararea unui tabel?";

xs1t = "a) TABLE";

xs2t = "b) ARRAY";

xs3t = "c) TR";

xs4t = "a";

break;

case 39:

xs0t = "Care sunt elementele HTML continute intr-un FORM:";

xs1t = "a) INPUT";

xs2t = "b) TYPE";

xs3t = "c) VALUE";

xs4t = "a";

break;

case 40:

xs0t = "Ce inseamna program CGI?";

xs1t = "a) program in limbaj C, ce utilizeaza interfete grafice";

xs2t = "b) program ce se executa pe serverul WWW";

xs3t = "c) program ce se executa pe site-ul client";

xs4t = "b";

break;

case 41:

xs0t = "Care sunt variabile de mediu CGI:";

xs1t = "a) CONTENT_TYPE";

xs2t = "b) QUERY_STRING";

xs3t = "c) REMOTE_SERVER";

xs4t = "abc";

break;

case 42:

xs0t = "Unde se apeleaza codul sursa in cazul programelor JAVASCRIPT?";

xs1t = "a) la site-ul serverului";

xs2t = "b) la site-ul clientului";

xs3t = "c) pe serverul proxy";

xs4t = "b";

break;

T

return x;

T

public static void main(Stringst args)

{

try

{

System.out.println("myServer.main: se creaza registrul!");

System.out.println("*");

System.out.println("Asteptati");

System.out.println("*");

LocateRegistry.createRegistry(2005);

System.out.println("myServer.main: se creaza serverul!");

myServer dateServer = new myServer();

System.out.println("*");

System.out.println("Asteptati");

System.out.println("*");

System.out.println("myServer.main: se inregistreaza serverul in registru!");

Naming.rebind("//:2005/DateServer", dateServer);

System.out.println("*");

System.out.println("Succes!!!Serverul ruleaza in background!");

System.out.println("Apasati CTRL+C pentru a inchide serverul");

System.out.println("*");

T

catch (Exception e)

{

System.out.println("Eroare: " + e.getMessage());

e.printStackTrace();

T

T

T

Aceasta interfata creeza mai int^i un registru folosind functia: LocateRegistry.createRegsitry(2005). Aceasta functie creeaza si porneste numele registrului care ruleaza pe masina locala la portul 2005. Ea returneaza o referire directa la registrul rezultat. Dupa ce a fost creat acest registru se creeaza un obiect de tipul myServer in felul urmator:

myServer Server = new myServer();

Dupa ce a fost creat acest obiect se incearca inregistrarea lui in registru sub un nume de serviciu in felul urmator:

Naming.rebind("//:2005/Server", Server);

Aceasta metoda contine o referire despre obiectul de la distanta specificat Server la adresa specificata ://2005/Server. Daca serverul a fost inregistrat atunci el poate primi cereri de la client.

15.2.3 Clasa Client

Acesta clasa numita Client mai int^i foloseste functia legareServer() care cauta serverul în numele registrului in felul urmator:

server = (interfata) Naming.lookup(serverName);

Metoda lookup() din clasa Naming este folosita pentru a primi o referire despre implementarea de la distanta a serverului. Variabila serverName este o variabila de tipul string si are forma:

String serverName = "rmi://localhost" + ":" + getParameter("registryPort") +

"/Server";

Daca serverul nu a fost gasit se genereaza o exceptie.

Aceasta clasa foloseste urmatoarele functii:

scrie(Graphics g) Aceasta functie afiseaza prezentarea, atunci c^nd se apasa butonul Credite.

init() Aceasta functie apeleaza mai int^i functia legareServer() descrisa mai sus, si apoi realizeaza prezentarea de la inceput.

afisare() Aceasta functie este apelata daca s-a apasat butonul Login. Ea realizeaza afisarea conditiilor acestui test. Daca se apasa butonul Start atunci se apeleaza functia init2() care afiseaza butoanele pentru a alege variantele de raspuns.

Clasa Client foloseste trei clase ajutatoare:

intrebare() Aceasa clasa afiseaza intrebarile care le primeste de la server, prelucreaza intrebarile, adica retine raspunsurile corecte, si afiseaza nota finala. Se poate trece la urmatoarea intrebare apas^nd butonul Urmatoarea Intrebare.

temp() Aceasta clasa contorizeaza numarul de intrebari. Atunci c^nd s-a ajuns la intrebarea 10 ea returneaza valoarea true si false in caz contrar.

fir() Aceasta clasa afiseaza timpul permis, adica 50 de secunde.

Implementarea clasei Client, desrisa mai sus, arata astfel:

import java.awt.*;

import java.applet.*;

import java.awt.event.*;

import java.net.URL;

import java.rmi.*;

import java.rmi.server.*;

public class client extends Applet implements ActionListener,ItemListener

{

private Label numLabel, afis, raspuns, prenumLabel, l, parolaLabel;

private Label sosire, p1, p2, p3, p3_1, p4, p5;

private TextField numEdit, prenumEdit, parolaEdit;

private Checkbox a, b, c;

private Button gata, buton, st, buton1;

private Intrebare intrebare;

private int numarIntrebare = 0;

private boolean apasatA, apasatB, apasatC, start = false;

private temp t = new temp();

private fir f;

private String nume, prenume, parola;

private interfata server;

public void scrie(Graphics g)

{

Font f;

f = new Font("courier", Font.BOLD, 24);

g.setFont(f);

g.setColor(Color.black);

g.fillRect(0, 0, 980, 500);

for (int i=0;i < 1010; i++)

{

g.setColor(Color.red);

g.drawString("Tema proiectului", 400 , 500-i);

g.drawString("Realizator", 445, 700-i);

g.drawString("Indrumator", 445, 900-i);

g.setColor(Color.yellow);

g.drawString("Magureanu Ionut", 405, 800-i);

g.drawString("Prof. Stefan Andrei", 390, 1000-i);

g.drawString("Test Navigare Internet bazat pe metoda RMI", 240 , 600-i);

try

{

Thread.sleep(50);

T

catch(InterruptedException exc)

{

System.err.println("Exceptie de la somn");

T

g.setColor(Color.black);

g.drawString("Realizator", 445, 700-i);

g.drawString("Indrumator", 445, 900-i);

g.drawString("Test Navigare Internet bazat pe metoda RMI", 240 , 600-i);

g.drawString("Tema proiectului:", 400, 500-i);

g.drawString("Magureanu Ionut", 405, 800-i);

g.drawString("Prof. Stefan Andrei", 390, 1000-i);

T

T

public void legareServer()

{

try

{

URL base = getDocumentBase();

String serverName = "rmi://localhost" + ":" + getParameter("registryPort") + "/DateServer";

System.out.println("Cautam serverul !!!");

server = (interfata) Naming.lookup(serverName);

System.out.println("Am gasit serverul!");

T

catch(Exception e)

{

System.out.println("Eroare:" + e.toString());

T

T

public void init()

{

legareServer();

setLayout(null);

Font font = new Font("Courier", Font.BOLD, 24);

setFont(font);

setBackground(Color.blue);

setForeground(Color.white);

numLabel = new Label("Numele de familie:");

add(numLabel);

numLabel.setBackground(Color.blue);

numLabel.reshape(200, 55, 270, 30);

numEdit = new TextField(10);

add(numEdit);

numEdit.setForeground(Color.black);

numEdit.setBackground(Color.white);

numEdit.addActionListener(this);

numEdit.reshape(500, 55, 200, 30);

prenumEdit = new TextField(10);

add(prenumEdit);

prenumEdit.setForeground(Color.black);

prenumEdit.setBackground(Color.white);

prenumEdit.reshape(500, 175, 200, 30);

prenumEdit.addActionListener(this);

prenumLabel = new Label("Prenumele:");

add(prenumLabel);

prenumLabel.setBackground(Color.blue);

prenumLabel.reshape(260, 175, 150, 30);

parolaLabel = new Label("Parola:");

add(parolaLabel);

parolaLabel.setBackground(Color.blue);

parolaLabel.reshape(290, 295, 100, 30);

parolaEdit = new TextField(10);

add(parolaEdit);

parolaEdit.setForeground(Color.black);

parolaEdit.setBackground(Color.white);

parolaEdit.addActionListener(this);

parolaEdit.reshape(500, 295, 200, 30);

parolaEdit.setEchoChar('*');

buton = new Button("Login");

buton.setForeground(Color.black);

add(buton);

buton.addActionListener(this);

buton.reshape(580, 420, 200, 30);

buton1 = new Button("Credite");

buton1.setForeground(Color.black);

add(buton1);

buton1.addActionListener(this);

buton1.reshape(200, 420, 200, 30);

intrebare = new Intrebare(server);

intrebare.init();

f = new fir(t, getGraphics(), intrebare);

T

public void init2()

{

raspuns = new Label("Raspuns:");

add(raspuns);

raspuns.reshape(250, 300, 100, 30);

raspuns.setForeground(Color.red);

a = new Checkbox("a)");

add(a);

a.addItemListener(this);

a.reshape(400, 290, 50, 50);

b = new Checkbox("b)");

add(b);

b.addItemListener(this);

b.reshape(550, 290, 50, 50);

c = new Checkbox("c)");

add(c);

c.addItemListener(this);

c.reshape(700, 290, 50, 50);

remove(st);

gata = new Button("Urmatoarea intrebare!");

add(gata);

gata.setForeground(Color.black);

gata.addActionListener(this);

gata.reshape(550, 440, 350, 30);

T

public void itemStateChanged(ItemEvent e)

{

if (e.getSource() == a)

apasatA = a.getState();

if (e.getSource() == b)

apasatB = b.getState();

if (e.getSource() == c)

apasatC = c.getState();

T

public void afisare()

{

sosire = new Label("Bine ati venit la acest test");

add(sosire);

sosire.setBackground(Color.blue);

sosire.reshape(150, 30, 400, 30);

Graphics text = getGraphics();

text.setColor(Color.cyan);

text.drawString(nume + " " + prenume + "!", 570, 51);

p1 = new Label("Reguli:");

add(p1);

p1.setForeground(Color.red);

p1.setBackground(Color.blue);

p1.reshape(100, 80, 100, 30);

p2 = new Label("- testul cuprinde 10 intrebari aleatoare dintr-un set de 42.");

add(p2);

p2.setForeground(Color.white);

p2.setBackground(Color.blue);

p2.reshape(30, 130, 900, 30);

p3 = new Label("- variantele de raspuns sunt a),b) si c). Se pot alege una, niciuna ");

add(p3);

p3.setBackground(Color.blue);

p3.reshape(30, 180, 950, 30);

p3_1 = new Label("sau mai multe variante de raspuns. ");

add(p3_1);

p3_1.setBackground(Color.blue);

p3_1.reshape(30, 230, 950, 30);

p4 = new Label("- timpul de raspuns este de 50 de secunde.");

add(p4);

p4.setBackground(Color.blue);

p4.reshape(30, 280, 950, 30);

p5 = new Label("S U C C E S !!!");

add(p5);

p5.setForeground(Color.cyan);

p5.setBackground(Color.blue);

p5.reshape(400, 350, 950, 30);

afis = new Label("Apasati start pentru a incepe testul!!!");

add(afis);

afis.setBackground(Color.blue);

afis.setForeground(Color.white);

afis.reshape(90, 420, 550, 30);

st = new Button("Start");

st.setForeground(Color.black);

add(st);

st.addActionListener(this);

st.reshape(680, 420, 200, 30);

T

public void sterge()

{

Graphics g = getGraphics();

g.clearRect(0, 0, 980, 500);

T

public void actionPerformed(ActionEvent e)

{

Graphics g = getGraphics();

boolean verificat = true;

if (e.getSource() == buton1)

{

remove(numLabel);

remove(prenumLabel);

remove(numEdit);

remove(prenumEdit);

remove(buton);

remove(parolaLabel);

remove(parolaEdit);

remove(buton1);

scrie(g);

init();

T

if (e.getSource() == buton)

{

nume = numEdit.getText().trim();

prenume = prenumEdit.getText().trim();

parola = parolaEdit.getText().trim();

try

{

verificat = server.verificare(nume, prenume, parola);

T

catch(RemoteException err)

{

System.out.println("Eroare: " + err.toString());

T

if (!verificat)

{

g.setColor(Color.red);

g.drawString("Date incorecte(nume invalide sau parola incorecta)!" ,160,380);

numEdit.setText("");

prenumEdit.setText("");

parolaEdit.setText("");

T

else

{

g.clearRect(0, 0, 980, 500);

remove(numLabel);

remove(prenumLabel);

remove(numEdit);

remove(prenumEdit);

remove(buton);

remove(buton1);

remove(parolaLabel);

remove(parolaEdit);

afisare();

T

T

if (e.getSource() == gata)

{

g.clearRect(0, 0, 980, 500);

if (t.getNr() > 0)

{

apasatA = a.getState();

apasatB = b.getState();

apasatC = c.getState();

intrebare.apasat(apasatA, apasatB, apasatC);

intrebare.prelucreaza(t.getNr());

T

if (!start)

{

start = true;

f.start();

T

else

f.setZero();

t.plus();

sterge();

if (t.getNr() <= 10)

{

intrebare.afiseazaIntrebare(t.getNr(), g, true);

// intarzie 50 de secunde (1000 unitati = 1 secunda)

try

{

Thread.sleep(50);

T

catch(InterruptedException exc)

{

System.err.println("Exceptie de la somn");

T

T

else

{

remove(gata);

remove(a);

remove(b);

remove(c);

remove(raspuns);

g.setColor(Color.cyan);

g.drawString(nume + " " + prenume, 400, 200);

intrebare.afiseazaNota(g);

f.stop();

T

a.setState(false);

b.setState(false);

c.setState(false);

T

if (e.getSource() == st)

{

remove(afis);

remove(sosire);

remove(p1);

remove(p2);

remove(p3);

remove(p3_1);

remove(p4);

remove(p5);

repaint();

init2();

if (!start)

{

start = true;

f.start();

T

t.plus();

sterge();

if (t.getNr() <= 10)

{

intrebare.afiseazaIntrebare(t.getNr(), g, true);

// intarzie 50 de secunde (1000 unitati = 1 secunda)

try

{

Thread.sleep(50);

T

catch(InterruptedException exc)

{

System.err.println("Exceptie de la somn");

T

T

a.setState(false);

b.setState(false);

c.setState(false);

T

T

public void paint(Graphics g)

{

if (t.getNr() > 0)

{

if (t.getNr() <= 10)

intrebare.afiseazaIntrebare(t.getNr(), g, false);

else

{

intrebare.afiseazaNota(g);

f.stop();

T

T

T

T

class Intrebare

{

private final int nrMax = 42;

private int intrebareaCurenta = 0;

private boolean st intreb = new booleansnrMax + 1t;

private boolean st nota = new booleansnrMax + 1t;

private boolean aRaspuns, bRaspuns, cRaspuns;

private boolean aApasat, bApasat, cApasat;

private interfata server;

public Intrebare(interfata server)

{

this.server = server;

T

public void init()

{

int i;

for (i = 0; i <= nrMax; i++)

intrebsit = false;

for (i = 0; i < 10; i++)

notasit = false;

T

public void apasat(boolean a, boolean b, boolean c)

{

aApasat = a;

bApasat = b;

cApasat = c;

T

public void prelucreaza(int nrIntrebare)

{

if (aRaspuns == aApasat && bRaspuns == bApasat && cRaspuns == cApasat)

notasnrIntrebare – 1t = true;

else

notasnrIntrebare – 1t = false;

T

public void afiseazaNota(Graphics g)

{

int i, Nota = 0;

for (i = 0; i < 10; i++)

if (notasit == true)

Nota ++;

g.setColor(Color.white);

switch (Nota)

{

case 0: case 1: case 2: case 3: case 4:

g.drawString("Ne vedem in toamna!!! Nota = " + Nota, 300, 250);

break;

case 5: case 6:

g.drawString("Aveti nota de trecere!!! Nota = "+Nota, 280, 250);

break;

case 7: case 8:

g.drawString("Bine, dar se putea si mai bine!!! Nota = "+Nota, 260, 250);

break;

case 9:

g.drawString("Se pare ca ati invatat mult!!! Nota = "+Nota, 280, 250);

break;

case 10:

g.drawString("Puteti deveni un adevarat profesor!!! Nota = "+Nota, 180, 250);

T

T

private int alegeIntrebarea()

{

// 42 reprezinta numarul total de intrebari !!

int numar = (int) (Math.random() * nrMax);

while (numar < nrMax && intrebsnumart == true)

{

numar ++;

if (numar == nrMax + 1)

numar = 0;

T

return numar;

T

public void afiseazaIntrebare(int nrIntrebare, Graphics g, boolean alta)

{

int nr;

if (alta)

{

nr = alegeIntrebarea();

intrebareaCurenta = nr;

T

else

nr = intrebareaCurenta;

if ((nr < 0) AA (nr > nrMax))

g.drawString("Scuze! Alegere gresita", 10,40);

intrebsnrt = true;

if (nrIntrebare<10)

g.drawString("" + nrIntrebare + ".", 25, 40);

else

g.drawString("" + nrIntrebare + ".", 10, 40);

Stringst intreb = new Strings5t;

try

{

intreb = server.getIntrebare(nr);

T

catch(RemoteException e)

{

System.out.println("Eroare !!! " + e.toString());

T

g.drawString(intrebs0t, 50, 40);

g.drawString(intrebs1t, 50, 90);

g.drawString(intrebs2t, 50, 140);

g.drawString(intrebs3t, 50, 190);

aRaspuns = bRaspuns = cRaspuns = false;

if (intrebs4t.indexOf("a") >= 0)

aRaspuns=true;

if (intrebs4t.indexOf("b") >= 0)

bRaspuns=true;

if (intrebs4t.indexOf("c") >= 0)

cRaspuns=true;

T

T

class temp

{

private boolean gata;

private int nrIntrebare;

public temp()

{

gata = false;

nrIntrebare = 0;

T

public boolean getGata()

{

return gata;

T

public void setGata(boolean v)

{

gata = v;

T

public void plus()

{

nrIntrebare++;

T

public int getNr()

{

return nrIntrebare;

T

T

class fir extends Thread

{

private temp t;

private Graphics g;

private int s, ss;

private boolean v;

private Intrebare intrebare;

public fir(temp t, Graphics g, Intrebare i)

{

this.t = t;

this.g = g;

s = 50;

ss = 0;

v = true;

intrebare = i;

T

public void sterge()

{

g.clearRect(0, 0, 980, 500);

T

public void setZero()

{

s = 50;

ss = 0;

T

private void timpExpirat()

{

if (t.getNr() > 0)

{

t.plus();

sterge();

if (t.getNr() <= 10)

{

intrebare.afiseazaIntrebare(t.getNr(), g, true);

T

else

{

intrebare.afiseazaNota(g);

v = false;

T

T

T

public void run()

{

while (v)

{

try

{

sleep(100);

T

catch(InterruptedException e)

{

System.out.println(e.toString());

T

if (ss > 0)

{

ss -= 10;

T

else

{

s–;

ss = 90;

T

g.clearRect(200, 430, 100, 40);

g.setColor(Color.cyan);

g.drawString("Timp ramas", 50, 460);

g.setColor(Color.red);

if (ss == 0)

g.drawString("" + s + ":00", 200, 460);

else

g.drawString("" + s + ":" + ss, 200, 460);

g.setColor(Color.black);

if (s == 0 && ss == 0)

{

timpExpirat();

setZero();

T

T

T

T

15.2.5 Executia aplicatiei

Pentru a rula aplicatia executam fisierul run.bat care realizeaza urmatoarele:

seteaza variabila CLASSPATH.

compileaza fisierele de tip java folosind comanda javac.

genereaza fisierele myServer_stub.class si myServer_skel.class folosind comanda rmic.

porneste serverul folosind comanda:

start java -Djava.security.policy=security.policy myServer

In acest moment serverul este pornit, adica: s-a creat un nou registru care asculta la portul 2005, s-a creat un nou obiect server si s-a inregistrat acest obiect in registru cu un nume de serviciu.

Pornim clientul folosind comanda appletviewer index.html.

Similar Posts