PZR420 - ćw - temat B
Obiekty rozproszone RMI w języku Java.
RMI = Remote Method Invocation
literatura i inne materiały ...
Krótki opis
technologii RMI
- Obiekty RMI to odległe obiekty rezydujące na odległych
maszynach.
- Klient na lokalnej maszynie może wywoływać metody obiektów RMI
tak jakby to były zwykłe lokalne obiekty.
- Obiekty RMI są tworzone przez serwer działający na odległej
maszynie.
![](../sop3225.gif)
.....................................................................................................................
- Klientowi "wydaje się" że odwołuje się do odległego obiektu, a
w rzeczywistości odwołuje się on do lokalnego obiektu zwanego pieńkiem
(ang. stub). Klasa pieńka jest tworzona przy pomocy kompilatora
rmic na podstawie klasy implementującej interfejs obiektu
RMI (patrz niżej).
- Podobnie obiekt RMI kontaktuje się bezpośrednio z tzw szkieletem
(?) (ang. skeleton), także tworzonym przy pomocy kompilatora
rmic.
![](../sop3227.gif)
.....................................................................................................................
- Obiekty RMI mogą być dostępne poprzez nazwę przy pomocy usługi
nazewniczej (ang. Naming Service) rmiregistry.
- Klient może zwrócić się do rmiregistry i otrzymać odniesienie
(referencję) do odległego obiektu RMI na podstawie podanej nazwy tego
obiektu.
- Serwer obiektu RMI musi wcześniej utworzyć, wyeksportować
i zarejestrować obiekt w rmiregistry.
![](../sop3224.gif)
.....................................................................................................................
Dla obiektu RMI trzeba zdefiniować interfejs
zawierający metody dostępne dla klientów, który dziedziczy z interfejsu
java.rmi.Remote: |
public interface OdleglyObiekt extends Remote
{
int powolnaMetoda(int skok) throws RemoteException;
}
.....................................................................................................................
Serwer musi zawierać klasę implementującą interfejs OdległyObiekt:
class OdleglaKlasa
implements OdleglyObiekt, Serializable
{
// konstruktor
OdleglaKlasa(String s) throws RemoteException
{
super();
try {
UnicastRemoteObject.exportObject(this);
// eksportujemy obiekt
// dzięki temu obiekt będzie "odległy"
// (exportObject() musi byc PRZED rebind())
Naming.rebind(s,this);
// rejestrujemy obiekt w rmiregistry
} catch(Exception e) {
System.err.println("sss: OdleglaKlasa(String); blad !!! "+e.getMessage());
}
// implementacja interfejsu OdleglyObiekt
private int licznik=0;
public int powolnaMetoda(int skok) throws RemoteException
{
for(int i=0; i<10; i++) {
licznik=licznik+skok;
System.out.println("sss: powolnaMetoda(); licznik="+licznik);
try {Thread.sleep(100);} catch(Exception e){};
}
return licznik;
}
}
.....................................................................................................................
Serwer musi wykonać następujące czynności:
- utworzyć obiekt - robi to w metodzie sss.main,
- wyeksportować go - robi to w konstruktorze OdleglaKlasa;
eksportowanie obiektu powoduje że staje się on odległym obiektem (gdyby
nie został wyeksportowany to odwołania klienta do tego obiektu
spowodowałyby kopiowanie obiektu od serwera do klienta i obiekt stałby
się lokalny !)
- zarejestrować w rmiregistry - robi to także w
konstruktorze OdleglaKlasa.
- po wykonaniu tych czynności serwer nie kończy działania lecz
oczekuje na klientów
public class sss
{
public static void main(String args[])
{
//System.setSecurityManager(new RMISecurityManager());
// chwilowo nie używamy SecurityManagera !
try {
OdleglyObiekt oo= new OdleglaKlasa("moj_obiekt");
// tworzymy obiekt
} catch(Exception e) {
System.err.println("sss: blad !!! "+e.getMessage());
}
}
}
Uwaga 1: Zamiast wywoływać jawnie
UnicastRemoteObject.exportObject() wystarczy że OdległaKlasa dziedziczy
z klasy UnicastRemoteObject, której konstruktor eksportuje obiekt
(jednak wtedy OdległaKlasa nie może dziedziczyć z żadnej innej klasy
...).
Uwaga 2: Jeśli
NIE dziedziczymy z UnicastRemoteObjecta to powinniśmy
samodzielnie przedefiniować w OdleglaKlasa niektóre metody klasy
Object: hashCode, equals, toString [w podanych przykładach tego się nie
robi ...]; patrz też klasa RemoteObject.
.....................................................................................................................
Klient musi wykonać następujące czynności:
- uzyskać referencję do obiektu RMI
- wywoływać metody na rzecz obiektu RMI
public class kkk
{
public static void main(String args[])
{
//System.setSecurityManager(new RMISecurityManager());
try {
OdleglyObiekt oo = (OdleglyObiekt)Naming.lookup(
"rmi://localhost/moj_obiekt");
/* uzyskujemy ref do obiektu RMI
trzeba oczywiście podać prawdziwy adres maszyny
na której działa rmiregistry i serwer
*/
System.out.println("kkk: oo.powolnaMetoda()="+oo.powolnaMetoda(1));
// wywołujemy metodę obiektu RMI
} catch(Exception e) {
System.err.println("kkk: blad !!! "+e.getMessage());
}
}
}
.....................................................................................................................
Klasy które muszą być dostępne (poprzez CLASSPATH) dla składników
aplikacji RMI:
- dla serwera:
OdleglyObiekt.class
OdleglaKlasa.class
OdleglaKlasa_Skel.class - to jest szkielet
(skeleton) serwera
// w JDK1.5 nie ma klas
*_Skel.java !
OdleglaKlasa_Stub.class
- dla klienta
OdleglyObiekt.class
OdleglaKlasa_Stub.class - to jest pieniek (stub)
klienta
- dla rmiregistry
to samo co dla serwera
Uwaga: jeśli używamy mechanizmu opisanego tutaj to nie wszystkie
klasy muszą być dostępne!
.....................................................................................................................
Czynności programisty podczas pisania aplikacji używającej
RMI:
(obrazek z książki Java & Corba)
![](../sop3222.jpg)
.....................................................................................................................
Co można przekazać do i co
może zwrócić metoda obiektu RMI :
public interface OdleglyObiekt extends Remote
{
PrzykladowaKlasa Eksperyment1(PrzykladowaKlasa pk1, PrzykladowaKlasa pk2)
throws RemoteException;
/* metoda zwraca obiekt klasy PrzykladowaKlasa który może być
obiektem odległym (wyeksportowanym) lub nie ...
w tym drugim przypadku obiekt zostanie skopiowany od serwera do klienta
metoda pobiera jako parametry dwa obiekty klasy PrzykładowaKlasa
które zostaną skopiowane od klienta do serwera
*/
}
- parametrami metod i zwracanymi wartościami mogą być
niestandardowe obiekty (nie należące do std klas Javy)
- zarówno serwer jak i klient musi mieć dostęp do klas obiektów
(PrzykładowaKlasa w powyższym przykładzie)
- jeśli wywołując metodę Eksperyment1() podamy jako parametr
obiekt podklasy PrzykładowaKlasa, to zarówno klient jak i
serwer musi mieć dostęp do kodu tej podklasy !
- jeśli pewna metoda obiektu RMI zwraca obiekt odległy (=
wyeksportowany) X, to obiekt ten nie zostanie skopiowany do
klienta; klient otrzyma jedynie ref do X; metody wywoływane na
rzecz X będą działać po stronie serwera !
- jeśli metoda obiektu RMI jest wywoływana z obiektem lokalnym
jako parametrem to obiekt ten zostanie "zserializowany" i przesłany do
serwera gdzie nastąpi odwrotny proces ("deserializacja"?); podobnie
jeśli metoda zwraca nieodległy obiekt; (przypomnijmy że "serializacja"
to zapis obiektu w postaci ciągu bajtów który w szczególności można
przesłać przez sieć; klasy implementujące interfejs Serializable są
automatycznie serializowane przez Javę).
.....................................................................................................................
Różnica między wyeksportowanym i nie-wyeksporowanym
obiektem znajdującym się po stronie serwera:
Zakładamy ze X jest obiektem RMI dostępnym dla klienta (np
za pośrednictwem rmiregistry). Niech Y będzie obiektem RMI po
stronie serwera dostępnym poprzez metodę X.drugiObiekt().
Co się stanie gdy klient wywoła X.drugiObiekt().metoda1(); ???
- jeśli Y został wyeksportowany to:
metoda1() będzie działać na odległym obiekcie Y (czyli po
stronie serwera)
- jeśli Y nie został wyeksportowany to:
obiekt Y zostanie skopiowany od serwera do klienta, i następnie
metoda1() będzie działać na lokalnym obiekcie !!!
.....................................................................................................................
Zadanie 10
Uruchom przykład programu RMI rmi4.zip, a
następnie:
- Zaprojektuj eksperyment który pokaże że jeśli 2 klientów
uruchamia metodę na rzecz tego samego obiektu RMI, to są oni
obsługiwani na osobnych wątkach (przerób implementację powolnaMetoda()
!).
- Sprawdź i opisz różnicę w działaniu tego programu gdy
drugiObiekt:
- jest eksportowany (czyli jest obiektem odległym)
- nie jest eksportowany
- Zbadaj jak są obsługiwane wyjątki które pojawiają się w
metodach obiektu RMI; w tym celu dodaj następującą metodę do interfejsu
OdległyObiekt:
public void generujeBlad() throws RemoteException
{
throw new
RemoteException("sztucznie wygenerowany blad ...");
}
i zobacz jak się ona zachowuje z pkt widzenia klienta ...
W sprawozdaniu umieść odp na pytania, kod, wydruki produkowane
przez programy.
Wskazówki do zadania 10:
Czynności podczas uruchamiania programu rmi4:
- klient i serwer mają własne katalogi ...
- w katalogu serwera:
- kompilujemy sss.java poleceniem:
javac sss.java
powstaną pliki:
OdleglaKlasa.class - klasa OdleglaKlasa
implementująca interfejs OdleglyObiekt
OdleglyObiekt.class - interfejs OdleglyObiekt
sss.class
- uruchamiamy kompilator rmic:
rmic OdległaKlasa
powstaną pliki:
OdleglaKlasa_Skel.class
OdleglaKlasa_Stub.class
- kopiujemy kody klas: OdległyObiekt.class oraz
OdległaKlasa_Stub.class do katalogu klienta
- uruchamiamy rmiregistry; rmiregistry MUSI mieć dostęp do
klas serwera poprzez zmienną środowiska CLASSPATH (zazwyczaj wystarczy
uruchomić rmiregistry z katalogu serwera!)
- uruchamiamy serwer poleceniem:
java sss
- w katalogu klienta:
- kompilujemy kod klienta
- uruchamiamy klienta poleceniem:
java kkk
Zadanie 11 ["Odległa" wersja
problemu producenta & konsumenta]
Zaprogramuj obiekt RMI "OdległyBufor" (nazwa interfejsu),
rozwiązujący problem producenta i konsumenta.
Obiekt RMI powinien mieć metody:
void WstawElement(int el)
int PobierzElement()
// jak widać do bufora wstawiamy jedynie liczby "int" !
Bufor przechowujacy elementy (typu FIFO) powinien byc w tablicy 50 integerow.
Powinien istnieć pomocniczy obiekt RMI "TwórzOdległeBufory" dostępny
przez nazwę "producent_konsument" w rmiregistry, zawierający metodę:
OdleglyBufor nowyOdległyBufor()
// mogą z niej korzystać dwa wątki tego samego procesu ...
tworzącą obiekty OdległyBufor na żądanie (cos w rodzaju "fabryki obiektow").
Przygotuj eksperyment pokazujący że to wszystko działa.
Niech producent produkuje liczby 1..1000, a konsument wyswietla co konsumuje.
Uwaga: producent i konsument musza byc (w zadaniu 11) watkami jednego programu, aby mialy dostep do wspolnego
odleglego bufora.
Do sprawozdania wstaw kody źródłowe klas oraz wydruki eksperymentów ...
Zadanie 12
Do poprzedniego zadania dodaj metodę:
OdleglyBufor nowyOdległyBuforZNazwa(String nazwa)
która pozwala korzystać z bufora dwóm różnym programom (dostają się
od
tego samego buforu podając tę samą nazwę).
.....................................................................................................................
Dynamiczne ładowanie
potrzebnych klas w RMI -
java.rmi.server.codebase
Opis
dynamicznego ładowania klas z dokumentacji java.sun.com.
Wstępne przypomnienie pewnych cech języka Java:
- mobilność kodu; można ściągnąć kod klasy np z Internetu,
następnie utworzyć obiekt tej klasy i używać go; istnieją wyrafinowane
mechanizmy bezpieczeństwa które zapewniają że obcy kod nie wykona
niebezpiecznych operacji (SecurityManager).
- właściwości; właściwość ma nazwę i wartość; program w
Javie może wyświetlić dostępne właściwości np tak:
Properties props=
System.getProperties();
props.list(System.out);
można zdefiniować właściwość podczas uruchamiania programu:
java
-Dnazwa_właściwości=wartość_właściwości klasa_główna
- SecurityManager zapewnia że aplikacja nie wykona
niebezpiecznych operacji; korzysta on z tzw "polityki", specjalnego
pliku definiującego co wolno robić aplikacji; przykładowo taka polityka:
grant { permission
java.security.AllPermission; };
daje aplikacji wszystkie możliwe prawa;
plik z polityką podaje się przy uruchamianiu aplikacji, za pomocą
właściwości java.security.policy w taki sposób:
java
-Djava.security.policy=plik.polityka klasa_główna
SecurityManager musi zostać włączony na początku metody main:
System.setSecurityManager(new RMISecurityManager());
// w tym
przypadku włączamy specyficznego Security Managera związanego z RMI ...
Klient i serwer aplikacji RMI muszą mieć dostęp do kodu wielu klas,
przykładowo:
- klient musi mieć dostęp do kodu klasy pieńka
- klient i serwer muszą mieć dostęp do wszystkich klas używanych
pośrednio i bezpośrednio w argumentach i w zwracanych wartościach metod
interfejsu odległego obiektu (patrz też tutaj)
RMI posiada specjalny mechanizm dynamicznego
ładowania potrzebnych klas przez sieć, przy czym klasy mogą być
ładowane w obu kierunkach: od serwera do klienta i w przeciwnym
kierunku! |
Aby mechanizm ten działał należy:
- serwer musi podać URL z którego mogą być ładowane klasy serwera
(w szczególności pieniek); URL podaje się w właściwości
java.rmi.server.codebase, np w taki sposób:
java
-Djava.rmi.server.codebase=file:///D:\Java\jdk1.2.2\Przyklad\rmi4a\SERWER\
klasa_serwera
//
pod Windows
java
-Djava.rmi.server.codebase=file:///home/mhanckow/katalog/ klasa_serwera
//
pod Linux-em
java
-Djava.rmi.server.codebase=http://qqq.www.eee/~jkowalski/moje_klasy
klasa_serwera
Uwaga: sciezka do katalogu
musi się kończyć przez "/" !
- podobnie klient musi podać URL do swoich klas (np klas obiektów
przekazywanych jako parametry metod odległego obiektu)
- jeżeli klient i serwer NIE pracują na tej samej maszynie i nie
mają dostępu do tego samego systemu plików to trzeba się posłużyć
jakimś niezależnym sposobem kopiowania plików, np serwerem httpd lub
ftp (wtedy użyjemy codebase=http://...);
- rmiregistry MUSI być tak uruchomione żeby NIE MIAŁO(!!!) dostępu
do klas serwera poprzez CLASSPATH
- interfejs odległego obiektu koniecznie musi mieć słowo "public"
(!!!)
- program, który przyjmuje obce klasy javowe MUSI mieć włączony
SecurityManager!
- [implementacja mechanizmu dynamicznego ładowania klas] gdy
obiekt jest przesyłany przez sieć to towarzyszy mu informacja GDZIE
można znaleźć kod klas tego obiektu, informację tę definiujemy właśnie
w codebase ...
.....................................................................................................................
Zadanie 13 [Zastosowanie
dynamicznego ładowania klas]
Do przykładu rmi4.zip dodaj zdalną metodę:
PrzykladowaKlasa Eksperyment1(PrzykladowaKlasa pk1, PrzykladowaKlasa pk2);
PrzykładowaKlasa powinna zawierać jakieś dane i metodę pokaz()
wyświetlającą te dane; ponadto konstruktor powinien wyświetlać napis
informujący o swoim uruchomieniu. Niech klient uruchamia tę metodę
podając jako parametry obiekt klasy
PrzykładowaKlasa, oraz obiekt podklasy
PrzykładowaKlasa o nazwie
PrzykładowaKlasa2. Metoda Eksperyment1() powinna uruchamiać pokaz() dla
swoich parametrów, podobnie klient powinien uruchomić pokaz() dla
obiektu zwróconego przez Eksperyment1().
Klasy których potrzebują klient i serwer powinny być dynamicznie
ściągane przy pomocy mechanizmu "codebase". Należy wykorzystać serwer
atos i katalog html_public i w nim (w odp podkatalogach) umieścić
wszystkie potrzebne klasy. Proszę spróbować uruchomić klienta i serwera
na innych maszynach (np na lokalnym linixie i na atosie, ew na 2
lokalnych linuxach). Do sprawozdania proszę oprócz kodu w Javie wstawić
także wydruki eksperymentów dowodzące że wszystko działa poprawnie ...
Zadanie
14 ["OdległyBufor" pozwalający wstawiać i pobierać dowolne
obiekty]
Rozwiąż zadanie odległy prod & kons
ale tym razem zezwala się na wstawianie i pobieranie dowolnych obiektów
(niekoniecznie liczb "int"); tak więc interfejs odległego obiektu
powinien mieć metody:
void WstawElement(Serializable el)
Serializable PobierzElement()
Koniecznie użyj mechanizmu "codebase" do kopiowania klas.
Zaprojektuj odp przykłady pokazujące że Twoje rozwiązanie działa
poprawnie ...
.....................................................................................................................
Zadanie 15 (*) [Model farmer/worker przy pomocy Java/RMI]
Zaprojektuj i zaprogramuj obiekt RMI który potrafi wykonywać zadania paralelizowalne,
tj takie które można podzielić na n- zadań wykonujących się równolegle
na osobnych wątkach.
Usługa ta ma byc uniwersalna tj NIE ma być przystosowana jedynie do
konkretnego zadania.
Zadanie podzielone na części przez klienta powinno przychodzić do
obiektu RMI w postaci
ciągu obiektów ze wspólnym interfejsem zawierającym metodę "Object
Oblicz()" (to są właśnie pracownicy).
Obiekt RMI (czyli farmer)
powinien uruchamiać metodę oblicz na osobnych wątkach; po zakończeniu
pracy przez
wszystkich pracowników powinien zwracac wynik w postaci listy obiektów
(klient będzie wiedział
co zrobić z tymi obiektami) ...
Jako zastosowanie rozwiąż problem znajdowania minimum funkcji, przy
czym podzadania dla workerów
są generowane przez rozłączne przedziały osi X na których workerzy
szukają minimum.
Wskazówka: obiekty z zadaniami
oraz wyniki zadań przesyłaj przy pomocy klasy Vector ...
Zadanie 16 (*) [Słownik w cyklu obiektów RMI]
Zaimplementuj słownik, którego elementy (czyli pary (klucz, wartość)) są trzymane w
listach,
które są w posiadaniu obiektów RMI,
które to obiekty tworzą cykl (tj każdy obiekt ma ref. do następnika i
do poprzednika).
Istnieje metoda szukaj(klucz),
zwracająca poszukiwaną wartość (ob. javy
zwracany przez wartość).
Jeśli wywołamy X.szukaj(klucz) dla pewnego ob. X w cyklu, to sprawdza
ona czy szukany element
znajduje się na liście ob. X, jeśli nie to mają być przeszukiwane listy
innych obiektów...
Istnieje też metoda dodaj(klucz,
wartość) dodająca element do listy wierzchołka cyklu tak
dobranego,
aby listy były zbalansowane (miały w miarę równe długości).
Dodatkowe wymagania:
1. odporność na usunięcie pojedynczego wierzchołka cyklu (cykl obiektów
się zmniejsza);
2. automatyczne ładowanie wszystkich potrzebnych klas;
3. obiekty cyklu znajdują się w różnych procesach (w chwili tworzenia
cyklu
można pomocniczo umieszczać ref do ob. w rejestrze,
ale zaraz potem usunąć te wpisy...).
Zadanie 17 [porównanie XDR i serializacji javowej]
Proszę podać jakieś istotne różnice w tych 2 sposobach serializacji
danych:
- XDR (używane w
SUN/ONC RPC)
- serializacja "javowa"
(używane w RMI)
na podstawie dowolnych materiałów, które można znaleźć w Internecie,
np. dokumentów RFC.
Można np. podać jakie są ograniczenia co do danych, które można
serializować...
Czy można serializować struktury danych z referencjami/wskazaniami?
Dlaczego w Javie nie zdecydowano się używać formatu XDR?
... itp kwestie można poruszyć.
Proszę też podać z jakich materiałów skorzystano.