Systemy rozproszone
Dr inż. L. Miękina
Department of Robotics and Mechatronics
AGH University of Science and Technology
Marzec, 2013
1/1
RMI - zdalne wywołanie metod
Rozproszone obiekty
Stan obiektu składa się z wartości jego pól. Zgodnie z paradygmatem
obiektowym, stan całego programu jest reprezentowany przez oddzielne części,
odpowiadające poszczególnym obiektom. Ponieważ obiektowo zorganizowane
programy są w ten sposób logicznie podzielone, fizyczne rozłożenie obiektów
pomiędzy różnymi procesami lub komputerami w systemie rozproszonym jest
naturalnym rozszerzeniem.
Rozproszone systemy obiektowe mogą być oparte na modelu klient-serwer. W
tym przypadku obiekty są zarządzane przez serwery i ich klienci wywołują ich
metody używając zdalnych wywołań metod. W RMI, żądanie klienta by
wywołać metodę obiektu zostaje wysłane jako komunikat do serwera
zawierającego obiekt. Wywołanie to jest realizowane przez metodę obiektu
serwera, a rezultat jest zwracany do klienta w osobnym komunikacie.
Ponieważ obiekty klientów i serwera są w różnych procesach, mamy do
czynienia z naturalną enkapsulacją. To znaczy, że stan obiektu może być
dostępny tylko za pomocą metod, co oznacza że nie jest możliwe by
nieuprawnione metody (zewnętrzne) wpływały na stan. Pozwala to również
odpowiednio rozwiązać zagadnienie równoczesnego dostępu, na przykład
stosując mechanizmy synchronizacji (semafory, sekcje krytyczne).
2 / 1
RMI
Rozproszony model obiektowy
Obiekty które mogą podlegać zdalnym wywołaniom są nazywane zdalnymi
obiektami. Aby umożliwić pracę ze zdalnymi obiektami wprowadzono szereg
rozszerzeń modelu obiektowego.
Każdy proces zawiera kolekcję obiektów, niektóre z nich mogą odbierać
zarówno zdalne jak i lokalne wywołania metod, podczas gdy pozostałe mogą
odbierać tylko lokalne wywołania, jak na schemacie poniżej.
wywołania metod między obiektami w różnych procesach, na tym samym
lub innym komputerze, są zdalne.
wywołania metod między obiektami w tym samym procesie są lokalnymi
wywołaniami metod.
3 / 1
RMI
Rozproszony model obiektowy
Zdalna referencja obiektu
Identyfikator, który może być używany w systemie rozproszonym w celu odwołania się
do wybranego unikalnego zdalnego obiektu.
Reprezentacja jej jest zwykle różna od lokalnej referencji obiektu. Składa się ona z:
adresu IP komputera i numeru portu związanego z procesem, który utworzył
obiekt
czasu utworzenia
lokalnego numeru obiektu i identyfikatora interfejsu (nazwy)
Zdalne referencje obiektu są podobne do lokalnych w tym że:
aby zdalny obiekt mógł podlegać RMI musi być określony za pomocą zdalnej
referencji obiektu
zdalne referencje obiektów mogą być przekazane jako argumenty i zwracane jako
rezultaty ze zdalnych wywołań metod
Zdalny interfejs
Interfejs, który deklaruje metody implementowane przez klasę zdalnego obiektu, na
przykład jako publiczne metody w języku Java.
Obiekty w innych procesach mogą wywoływać tylko te metody, które należą do
zdalnego interfejsu, podczas gdy lokalne obiekty mogą wywoływać metody zdalnego
interfejsu jak również inne publiczne metody implementowane przez zdalny obiekt.
4 / 1
RMI
Architektura
Zdalne wywołanie metod jest realizowane przez szereg obiektów i modułów.
Ilustruje to poniższy schemat, gdzie obiekt A aplikacji klienta wywołuje metodę
w zdalnym obiekcie B aplikacji serwera, dla którego posiada zdalną referencję
obiektu.
Po obu stronach połączenia istnieją: moduł zdalnych referencji i moduł
komunikacyjny. RMI działa w oparciu o nie.
5 / 1
RMI
Architektura - moduł komunikacyjny
Oba moduły komunikacyjne realizują protokół request-reply, który transmituje
komunikaty request i reply między klientem i serwerem. Struktura
komunikatów request i reply jest następująca:
Moduł komunikacyjny używa jedynie 3 pierwszych pól, które określają typ
komunikatu, jego identyfikator i zdalną referencję obiektu, którego metoda ma
być wywołana. Identyfikacja metody i serializacja argumentów spoczywa na
oprogramowaniu RMI.
Moduł komunikacyjny serwera wybiera dyspozytora klasy obiektu docelowego
wywołania, przekazując mu lokalną referencję, która jest udostępniona przez
moduł zdalnych referencji w odpowiedzi na zapytanie o zdalny identyfikator
obiektu.
6 / 1
RMI
Architektura - moduł zdalnych referencji
Moduł zdalnych referencji (MZR) odpowiada za translację między lokalnymi i
zdalnymi referencjami obiektu i za tworzenie zdalnych referencji obiektu. W
tym celu MZR w każdym procesie ma tablicę zdalnych obiektów (TZO), która
odzwierciedla powiązania między lokalnymi referencjami obiektów tego procesu
i zdalnymi referencjami obiektów (które są unikalne w obrębie systemu
rozproszonego). Tablica ta posiada:
wpisy dla wszystkich zdalnych obiektów udostępnianych przez proces, np.
zdalny obiekt B jest zapisany w TZO serwera
wpisy dla wszystkich lokalnych pośredników (proxy), np. proxy dla obiektu
B jest zapisany w TZO klienta
Działanie MZR:
kiedy zdalny obiekt ma być przekazany jako argument lub rezultat po raz
pierwszy, MZR tworzy zdalną referencję obiektu i dodaje ją do TZO
kiedy zdalna referencja obiektu jest odebrana w komunikacie request lub
reply, MZR jest pytany o odpowiadającą lokalną referencję obiektu, która
może odnosić się albo do proxy albo do zdalnego obiektu. Jeśli zdalna
referencja obiektu nie występuje w TZO, tworzy się nowe proxy i MZR
zapisuje je do swojej TZO.
7 / 1
RMI
Warstwa RMI - pośrednik (proxy)
Warstwa RMI występuje pomiędzy obiektami aplikacji a modułami
komunikacyjnym i zdalnych referencji.
Proxy
Rola pośrednika polega na tym, by uczynić zdalne wywołanie metod
przezroczystym dla klienta, poprzez stworzenie wrażenia, że proxy jest lokalnym
obiektem, choć w praktyce zamiast wywołać metodę tego obiektu, pośrednik
przekazuje to wywołanie w komunikacie do zdalnego obiektu.
Pośrednik ukrywa szczegóły zdalnej referencji obiektu, serializacji argumentów i
de-serializacji rezultatów, a także wysyłania i odbioru komunikatów.
Dla każdego zdalnego obiektu, dla którego proces posiada zdalną referencję
istnieje oddzielny pośrednik. Klasa pośrednika implementuje metody
zadeklarowane w zdalnym interfejsie zdalnego obiektu, który reprezentuje. To
gwarantuje, że zdalne wywołania metod są odpowiednie do typu zdalnego
obiektu. Jednakże pośrednik implementuje metody całkiem inaczej - każda
metoda pośrednika serializuje referencję do obiektu docelowego, swój własny
identyfikator i argumenty wywołania, a następnie wysyła je do obiektu
docelowego, czeka na komunikat zwrotny (reply), deserializuje go i zwraca
rezultat do metody wywołującej.
8 / 1
RMI
Warstwa RMI - dyspozytor (dispatcher) i szkielet (skeleton)
Dyspozytor
Serwer posiada po jednym dyspozytorze i szkielecie dla każdej klasy
reprezentujacej zdalny obiekt, w naszym przykładzie serwer ma dyspozytora i
szkielet dla klasy zdalnego obiektu B. Dyspozytor odbiera komunikat request z
modułu komunikacyjnego. Na podstawie pola methodId wybiera odpowiednią
metodę szkieletu, przekazując jej komunikat request. Dyspozytor i pośrednik
używają tych samych identyfikatorów methodId do metod zdalnego interfejsu.
Szkielet
Klasa zdalnego obiektu posiada szkielet, który implementuje metody
zadeklarowane w zdalnym interfejsie. Są one zaimplementowane całkiem
inaczej niż metody zdalnego obiektu. Metoda szkieletu de-serializuje argumenty
komunikatu request i wywołuje odpowiednią metodę zdalnego obiektu.
Następnie czeka na zrealizowanie wywołania i serializuje jego rezultat, razem z
ewentualnymi wyjątkami, w komunikat reply do metody pośrednika, który
inicjował wywołanie.
9 / 1
RMI
Generowanie klas pośredników, dyspozytorów i szkieletów
Klasy pośredników, dyspozytorów i szkieletów są generowane automatycznie przez
kompilator interfejsu (interface compiler).
Dla Java RMI, metody zdalnego obiektu są deklarowane jako interfejsy języka
Java i są implementowane przez klasę zdalnego obiektu. Kompilator interfejsu
języka Java RMI rmic generuje klasy namiastek (stub) w oparciu o klasę
zdalnego obiektu.
Dla implementacji standardu CORBA nazywanej Orbix, interfejsy zdalnych
obiektów są definiowane w języku CORBA IDL, a następnie używa się
kompilatora interfejsu do generowania klas pośredników, dyspozytorów i
szkieletów w C++ lub innych językach. Obiekty CORBA są opisane za pomocą
interfejsów wyrażonych w języku Interface Definition Language (IDL),
zaprojektowanym przez OMG do definiowania interfejsu obiektów. Interfejs
obiektu określa operacje które obiekt realizuje, ale nie określa sposobu
implementacji. Implementacja obiektu CORBA jest domeną wybranego języka
programowania, np. Java lub C++. Interfejs określa kontrakt wiążący kod
użytkownika obiektu i kod implementujący obiekt.
Interfejsy IDL są niezależne od języka programowania. IDL definiuje powiązania
z wieloma językami programowania (C, C++, Java, Ada, COBOL, Smalltalk,
Objective C i Lisp). Pozwala to wybrać dla implementacji obiektów najbardziej
odpowiedni język, jak również nie ogranicza programistów klienta co do wyboru
identycznego języka jaki użyto dla obiektu.
10 / 1
RMI
Programy serwera i klientów
Program serwera zawiera klasy dyspozytorów i szkieletów, razem z
implementacjami klas wszystkich zdalnych obiektów, które udostępnia
serwer. Dodatkowo program serwera zawiera sekcję inicjalizacji, zwykle w
funkcji main kodu Java lub C++. Sekcja inicjalizacji odpowiada za
tworzenie i inicjalizację co najmniej jednego zdalnego obiektu, spośród
obiektów serwera; dodatkowe zdalne obiekty mogą być tworzone w
odpowiedzi na żądania klientów. Sekcja inicjalizacji może ponadto
rejestrować niektóre zdalne obiekty w tzw. binderze. Generalnie
rejestrowany będzie tylko jeden zdalny obiekt, przez który realizowany jest
dostęp do pozostałych.
Program klienta zawiera klasy pośredników dla wszystkich zdalnych
obiektów, które będzie wywoływać. Klient może używać bindera do
wyszukiwania zdalnych referencji obiektów.
Binder w systemie rozproszonym jest oddzielną usługą, która zarządza
tablicą zawierającą odwzorowania nazw (tekstowych) na zdalne referencje
obiektów. Usługi tej używają serwery do rejestrowania swoich zdalnych
obiektów pod odpowiednimi nazwami, a klienci do wyszukiwania obiektów
za pomocą nazw. Przykłady usług typu binder: Java RMIregistry i
CORBA Naming Service
11 / 1
Java RMI
Java API dla RMI
Pierwsza wersja Java RMI używała wszystkich klas pokazanych na diagramie
architektury. Począwszy od Javy w wersji 1.2 zastosowano mechanizm refleksji do
implementacji uniwersalnego dyspozytora i eliminacji szkieletów klas usługowych.
Klasy pośredników klienta (proxy) są generowane przez kompilator rmic na podstawie
skompilowanych klas usługowych (servant), a nie na podstawie definicji zdalnego
interfejsu.
Rys. powyżej pokazuje strukturę dziedziczenia klas obsługujących serwery Java RMI.
Jedyną klasą istotną dla programisty jest UnicastRemoteObject, z której musi
dziedziczyć każda klasa usługowa (servant). Klasa UnicastRemoteObject dziedziczy z
abstrakcyjnej klasy RemoteServer, która deklaruje abstrakcyjne wersje metod
wymaganych przez zdalne serwery. Inną podklasą RemoteServer jest Activatable,
która dostarcza możliwości automatycznej aktywacji obiektu serwera, kiedy zajdzie
taka potrzeba.
12 / 1
Java RMI
Użycie mechanizmu refleksji
Refleksja jest używana do przekazywania w komunikacie request informacji o
wywoływanej metodzie. Odbywa się to za pomocą klasy Method pakietu reflection.
Każda instancja klasy Method reprezentuje charakterystykę konkretnej metody (jej
klasę, typy argumentów, zwracanej wartości i wyjątków). UWAGA: instancja klasy
Method może być uruchomiona dla obiektu odpowiedniej klasy za pomocą jej metody
invoke. Metoda invoke wymaga dwu argumentów:
pierwszy specyfikuje wywoływany obiekt
drugi jest tablicą elementów typu Object, zawierającą argumenty
Rezultat jest zwracany jako typ Object.
Kolejność operacji:
Pośrednik (proxy) szereguje informacje dot. metody i jej argumentów w
komunikat request. Dane metody szereguje się w postaci obiektu klasy Method.
Następnie pakuje się argumenty w tablicy elementów typu Object i szereguje się
tę tablicę.
Dyspozytor de-szereguje obiekt klasy Method i argumenty z tablicy elementów
typu Object z komunikatu request.
Jak zwykle, zdalna referencja obiektu docelowego jest de-szeregowana i
odpowiednia lokalna referencja obiektu jest odczytana z modułu zdalnych
referencji.
Dyspozytor wywołuje metodę invoke obiektu klasy Method, z parametrami
oznaczającymi obiekt docelowy i tablicę wartości argumentów.
13 / 1
Java RMI
Przekazywanie parametrów i rezultatu
Dowolny serializowalny obiekt - tzn. taki który implementuje interfejs Serializable -
może być przekazany jako argument lub rezultat. Wszystkie typy proste i zdalne
obiekty są serializowalne. Klasy argumentów i rezultatu są ładowane w razie potrzeby
do odbiorcy przez system RMI.
Parametry i rezultaty mogą być przekazywane na dwa sposoby:
Przez referencję. Jeśli typ parametru lub rezultatu jest definiowany jako zdalny
interfejs, odpowiedni argument lub rezultat jest zawsze przekazywany jako zdalna
referencja obiektu. Np., w 3 przykładzie RMI, metoda newShape zwraca rezultat
typu Shape - czyli zdalny interfejs. Kiedy zdalna referencja obiektu jest odebrana,
może być ona użyta w wywołaniu RMI zdalnego obiektu, do którego się odnosi.
Przez wartość. Wszystkie serializowalne nie-zdalne obiekty są kopiowane i
przekazywane przez wartość. Np., w 2 przykładzie RMI, argument metody add i
rezultat metody get są typu Event, (który jest serializowalny) i są przekazywane
przez wartość. Gdy obiekt jest przekazywany przez wartość, nowy obiekt jest
tworzony w procesie odbiorcy. Metody tego nowego obiektu mogą być
wywoływane lokalnie, powodując zmianę stanu w stosunku do oryginalnego
obiektu w procesie nadawcy.
Argumenty i zwracane wartości są serializowane do strumienia z użyciem
następujących reguł:
gdy serializowany jest obiekt który implementuje interfejs Remote, jest on
zastępowany przez zdalną referencję obiektu, która zawiera nazwę klasy obiektu.
gdy serializowany jest dowolny obiekt, informacja o jego klasie jest uzupełniona o
lokalizację klasy (URL), co umożliwia ładowanie klasy do procesu odbiorcy.
14 / 1
Java RMI
Bezpieczeństwo
Począwszy od wersji 1.2 języka Java istnieje możliwość definiowania przez użytkownika
zabezpieczeń maszyny wirtualnej Java. Zabezpieczenia te mogą obejmować:
definiowanie akceptowanych połączeń sieciowych (adresów IP, numerów portów)
uprawnień do odczytu lub zapisu plików przez zewnętrzne aplikacje
uprawnień do ładowania kodu z innych maszyn wirtualnych Java.
Ostatni przypadek jest szczególnie ważny, gdyż przekazanie obiektu typu Serializable
przez wartość powoduje załadowanie jego kodu i możliwość (a czasem ryzyko)
wykonywania jego metod.
W przypadku ładowania kodu z zewnątrz maszyna wirtualna Java wręcz wymaga, aby
twórca aplikacji zdefiniował i wdrożył zasady bezpieczeństwa. Jeśli nie zostanie to
zrobione, wykonanie programu zakończy następujący wyjątek:
java.security.AccessControlException: access denied \
(java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
Definicja polityki bezpieczeństwa powinna się znalezć w pliku o nazwie java.policy.
Taki plik znajduje się zazwyczaj w podkatalogu jre/lib/security katalogu instalacji
środowiska Java, ale najczęściej nie jest używany do uruchamiania aplikacji, gdyż
zawiera tylko zbiór domyślnych reguł.
15 / 1
Java RMI
Bezpieczeństwo
Zazwyczaj twórca aplikacji definiuje własny plik java.policy, którego następnie używa
przy uruchamianiu aplikacji. Najprostsza możliwa zawartość pliku java.policy, nie
zabezpieczająca maszyny wirtualnej w żaden sposób, jest następująca:
1grant {
2 // Pelne uprawnienia dla wszystkich ladowanych zdalnie klas
3 permission java.security.AllPermission;
4};
Za odczytanie i realizowanie w programie Java zdefiniowanych wcześniej zasad
bezpieczeństwa odpowiadają klasy tzw. zarządców bezpieczeństwa:
ogólna SecurityManager
specjalizowana (dla RMI) RMISecurityManager.
Zarządca bezpieczeństwa powinien zostać utworzony na samym początku funkcji
main, na przykład w następujący sposób:
3 public static void main(String args[]){
4 if(System.getSecurityManager() == null)
5 System.setSecurityManager(new RMISecurityManager());
16 / 1
Java RMI
Przykład - wymagania funkcjonalne
Przykład dotyczy aplikacji prostego monitora komunikatów, który odbiera komunikaty
tekstowe od klientów i wyświetla je na ekranie serwera.
Funkcjonalność:
Serwer obsługuje listę odebranych komunikatów, dostarczając klientom operacji
do przesyłania komunikatów tekstowych.
Każdy odebrany komunikat jest wyświetlany przez serwer.
17 / 1
Java RMI
Przykład - zdalny interfejs
Zdalne interfejsy są definiowane przez dziedziczenie z interfejsu Remote dostępnego w
pakiecie java.rmi. Metody interfejsu muszą generować wyjątek RemoteException, a
poza tym dowolne wyjątki specyficzne dla aplikacji.
Przykład zdalnego interfejsu:
1import java.rmi.*;
2
3public interface MonitorInterface extends Remote {
4 void komunikuj(String message) throws RemoteException;
5 boolean zarejestruj(String nick) throws RemoteException;
6}
Ten zdalny interfejs deklaruje tylko dwie metody (komunikuj i zarejestruj), które
posiadają jeden parametr typu String, przez który przekazywany jest łańcuch
reprezentujący komunikat i nazwę użytkownika.
18 / 1
Java RMI
Przykład - kod serwera
Kod serwera składa się z klas MonitorServer i MonitorServant.
MonitorServer jest klasą główną i składa się z dwu pól i dwu metod. Pola są
obiektami klas:
Registry, który reprezentuje rmiregistry
MonitorServant, który reprezentuje klasę usługową (servant).
Metoda main serwera tworzy instancję klasy MonitorServer. Jeśli wystąpi jakikolwiek
wyjątek, wyświetlany jest stos wywołań i kończy się działanie serwera.
1import java.rmi.RemoteException;
2import java.rmi.registry.LocateRegistry;
3import java.rmi.registry.Registry;
4import java.rmi.server.UnicastRemoteObject;
5
6public class MonitorServer {
7 Registry reg; /* rejestr nazw obiektow */
8 MonitorServant servant;
9
10 public static void main(String[] args) {
11 try {
12 MonitorServer s = new MonitorServer();
13 } catch (Exception e) {
14 e.printStackTrace(); System.exit(1);
15 }
16 }
19 / 1
Java RMI
Przykład - kod serwera
Chroniony konstruktor klasy MonitorServer najpierw tworzy zdalny obiekt
rejestru, pracujący na wybranym porcie. Następnie tworzy instancję klasy
MonitorServant i wiąże ją z pewną nazwą w rejestrze RMIregistry. Wartość
reprezentująca to powiązanie jest zdalną referencją obiektu, a jej typ jest typem
jego zdalnego interfejsu - MonitorInterface.
18 protected MonitorServer() throws RemoteException {
19 try {
20 /* Utworzenie rejestru nazw */
21 reg = LocateRegistry.createRegistry(1099);
22 /* utworzenie obiektu dostepnego zdalnie */
23 servant = new MonitorServant();
24 /* zwiazanie z obiektem nazwy */
25 reg.rebind("MonitorServer", servant);
26 System.out.println("Server READY");
27 } catch(RemoteException e) {
28 e.printStackTrace(); throw e;
29 }
30 }
31}
20 / 1
Java RMI
Przykład - kod klasy usługowej MonitorServant
1import java.rmi.RemoteException;
2import java.rmi.registry.LocateRegistry;
3import java.rmi.registry.Registry;
4import java.rmi.server.UnicastRemoteObject;
5
6public class MonitorServant
7 extends UnicastRemoteObject
8 implements MonitorInterface {
9
10 public MonitorServant() throws RemoteException {
11 }
12
13 // Metoda implementujaca funkcje komunikuj() interfejsu MonitorInterface
14 public void komunikuj(String message) throws RemoteException {
15 System.out.println("Server.komunikuj() " + message);
16 }
17 // Metoda implementujaca funkcje zarejestruj() interfejsu MonitorInterface
18 public boolean zarejestruj(String nick) throws RemoteException {
19 System.out.println("Server.zarejestruj() " + nick);
20 return true;
21 }
22}
21 / 1
Java RMI
Przykład - kod klasy klienta MonitorClient
1import java.rmi.NotBoundException;
2import java.rmi.RemoteException;
3import java.rmi.registry.LocateRegistry;
4import java.rmi.registry.Registry;
5
6public class MonitorClient {
7 public static void main(String[] args) {
8 if(args.length < 2) {
9 System.out.println("Usage: MonitorClient
");
10 System.exit(-1);
11 }
12 MonitorInterface remoteObject; // referencja do zdalnego obiektu
13 Registry reg; // rejestr nazw obiektow
14 try {
15 // pobranie referencji do rejestru nazw obiektow
16 reg = LocateRegistry.getRegistry(args[0]);
17 // odszukanie zdalnego obiektu po jego nazwie
18 remoteObject = (MonitorInterface) reg.lookup("MonitorServer");
19 // wywolanie metody zdalnego obiektu
20 remoteObject.komunikuj(args[1]);
21 remoteObject.zarejestruj("Luc");
22 }
23 catch(RemoteException e) {
24 e.printStackTrace();
25 }
26 catch(NotBoundException e) {
27 e.printStackTrace();
28 }
29 }
30}
22 / 1
Java RMI
Przykład - kompilacja i uruchomienie
W celu kompilacji i uruchomienia rozproszonej aplikacji należy wykonać:
1
Kompilacja klasy serwera
lm@lm-mint ~ $ javac MonitorServer.java
2
Kompilacja klasy klienta
lm@lm-mint ~ $ javac MonitorClient.java
3
Generowanie namiastki zdalnego obiektu, w wyniku czego powstaje bajtkod
zawarty w pliku MonitorServant_Stub.class
lm@lm-mint ~ $ rmic MonitorServant
4
Uruchomienie serwera
lm@lm-mint ~ $ java MonitorServer &
Server READY
5
Uruchomienie klienta, aby umieścić na serwerze nowy komunikat
lm@lm-mint ~ $ java MonitorClient localhost s1
Server.komunikuj() s1
Server.zarejestruj() Luc
23 / 1
Java RMI
Przykład 2 - rozbudowany monitor
Przykład dotyczy aplikacji nieco rozbudowanego monitora komunikatów, który odbiera
komunikaty tekstowe od klientów i wyświetla je na ekranie serwera, a ponadto
umożliwia przeglądanie przez klientów zarejestrowanych komunikatów.
Funkcjonalność:
Serwer obsługuje listę odebranych komunikatów, dostarczając klientom operacji
do przesyłania komunikatów tekstowych. Każdy odebrany komunikat jest
wyświetlany i rejestrowany wraz z datą przez serwer.
Serwer ponadto udostępnia operacje umożliwiające klientom odczyt ilości
komunikatów, wybranego komunikatu i całej ich listy.
24 / 1
Java RMI
Przykład 2 - zdalny interfejs
Zdalne interfejsy są definiowane przez dziedziczenie z interfejsu Remote dostępnego w
pakiecie java.rmi. Metody interfejsu muszą generować wyjątek RemoteException, a
poza tym dowolne wyjątki specyficzne dla aplikacji.
Przykład zdalnego interfejsu:
1import java.rmi.*;
2import java.util.Vector;
3
4public interface MonitorInterface extends Remote {
5 void Inform(String message) throws RemoteException;
6 Event get(int i) throws RemoteException;
7 void add(Event event) throws RemoteException;
8 int getCount() throws RemoteException;
9 Vector allEvents() throws RemoteException;
10}
Zdalny interfejs deklaruje następujące metody:
inform, która posiada jeden parametr o nazwie message, przez który
przekazywany jest łańcuch reprezentujący komunikat.
get do odczytu komunikatu zapisanego pod wybranym indeksem
add do rejestracji nowego komunikatu przez serwer
getCount do odczytu ilości zarejestrowanych komunikatów
allEvents do odczytu wszystkich zarejestrowanych komunikatów
25 / 1
Java RMI
Przykład 2 - kod serwera
Kod serwera składa się z klas MonitorServer i MonitorServant.
MonitorServer jest klasą główną i składa się z dwu pól i dwu metod.
Pola są instancjami klas:
Registry, który reprezentuje rmiregistry
MonitorServant, który reprezentuje klasę usługową (servant).
Metoda main serwera tworzy instancję klasy MonitorServer. Jeśli wystąpi jakikolwiek
wyjątek, wyświetlany jest stos wywołań i kończy się działanie serwera.
1import java.rmi.RemoteException;
2import java.rmi.registry.LocateRegistry;
3import java.rmi.registry.Registry;
4import java.rmi.server.UnicastRemoteObject;
5
6public class MonitorServer {
7 Registry reg; /* rejestr nazw obiektow */
8 MonitorServant servant;
9
10 public static void main(String[] args) {
11 try {
12 MonitorServer s = new MonitorServer();
13 } catch (Exception e) {
14 e.printStackTrace(); System.exit(1);
15 }
16 }
26 / 1
Java RMI
Przykład 2 - kod serwera
Chroniony konstruktor klasy MonitorServer najpierw tworzy zdalny obiekt rejestru,
pracujący na wybranym porcie. Nastepnie tworzy instancję klasy MonitorServant i
wiąże ją z pewną nazwą w rejestrze RMIregistry. Wartość reprezentująca to
powiązanie jest zdalną referencją obiektu, a jej typ jest typem jego zdalnego interfejsu
- MonitorInterface.
18 protected MonitorServer() throws RemoteException {
19 try {
20 /* Utworzenie rejestru nazw */
21 reg = LocateRegistry.createRegistry(1099);
22 /* utworzenie obiektu dostepnego zdalnie */
23 servant = new MonitorServant();
24 /* zwiazanie z obiektem nazwy */
25 reg.rebind("MonitorServer", servant);
26 System.out.println("Server v2 READY");
27 } catch(RemoteException e) {
28 e.printStackTrace(); throw e;
29 }
30 }
31}
27 / 1
Java RMI
Przykład 2 - kod klasy usługowej MonitorInterface
1import java.rmi.RemoteException;
2import java.rmi.registry.LocateRegistry;
3import java.rmi.registry.Registry;
4import java.rmi.server.UnicastRemoteObject;
5import java.util.Vector;
6
7public class MonitorServant extends UnicastRemoteObject implements
8 MonitorInterface {
9
10 private Vector events;
11
12 public MonitorServant() throws RemoteException {
13 events = new Vector();
14 }
15
16 // Metoda implementujaca funkcje Inform() interfejsu MonitorInterface
17 public void Inform(String message) throws RemoteException {
18 events.add(new Event(message));
19 System.out.println("Server: " + message);
20 }
21
22 public Event get(int i) throws RemoteException {
23 if (i < 0 || i >= events.size())
24 return null;
25 return events.get(i);
26 }
28 / 1
Java RMI
Przykład 2 - kod klasy usługowej MonitorInterface
28 public void add(Event event) throws RemoteException {
29 events.add(event);
30 }
31
32 public int getCount() throws RemoteException {
33 return events.size();
34 }
35
36 public Vector allEvents() throws RemoteException {
37 return events;
38 }
39
40}
29 / 1
Java RMI
Przykład - kod klasy klienta MonitorClient
1import java.rmi.NotBoundException;
2import java.rmi.RemoteException;
3import java.rmi.registry.LocateRegistry;
4import java.rmi.registry.Registry;
5import java.util.Vector;
6
7public class MonitorClient {
8
9 public static void main(String[] args) {
10 if(args.length < 2) {
11 System.out.println("Usage: MonitorClient ");
12 System.exit(-1);
13 }
14 MonitorInterface remoteObject; // referencja do zdalnego obiektu
15 Registry reg; // rejestr nazw obiektow
16 try {
17 // pobranie referencji do rejestru nazw obiektow
18 reg = LocateRegistry.getRegistry(args[0]);
19 // odszukanie zdalnego obiektu po jego nazwie
20 remoteObject = (MonitorInterface) reg.lookup("MonitorServer");
21 // wywolanie metody zdalnego obiektu
22 remoteObject.Inform(args[1]);
23 remoteObject.add(new Event("Event sent from client"));
24 int cnt = remoteObject.getCount();
25 System.out.println("There are " + cnt + " registered events:");
26 Vector e = remoteObject.allEvents();
27 for(Event ev : e)
28 ev.print();
29 Event back = remoteObject.get(0);
30 back.print();
31 } catch(RemoteException e) {
32 e.printStackTrace();
33 } catch(NotBoundException e) {
30 / 1
34 e.printStackTrace();
Java RMI
Przykład 2 - kompilacja i uruchomienie
W celu kompilacji i uruchomienia rozproszonej aplikacji należy wykonać:
1
Kompilacja klasy serwera
lm@lm-mint ~$ javac MonitorServer.java
2
Kompilacja klasy klienta
lm@lm-mint ~$ javac MonitorClient.java
3
Generowanie namiastki zdalnego obiektu, w wyniku czego powstaje bajtkod
zawarty w pliku MonitorServant_Stub.class.
lm@lm-mint ~$ rmic MonitorServant
4
Uruchomienie serwera
lm@lm-mint ~$ java MonitorServer &
Server v2 READY
5
Uruchomienie klienta aby umieścić na serwerze nowe komunikaty
lm@lm-mint ~$ java MonitorClient localhost s1
Server: s1
There are 1 registered events:
Mon May 02 23:16:34 CEST 2011: s1
lm@lm-mint ~$ java MonitorClient localhost s2
Server: s2
There are 2 registered events:
Mon May 02 23:16:34 CEST 2011: s1
Mon May 02 23:16:51 CEST 2011: s2
31 / 1
Wyszukiwarka
Podobne podstrony:
Wyklad SR 4
Wyklad SR 1,2
Sieci komputerowe wyklady dr Furtak
Wykład 05 Opadanie i fluidyzacja
WYKŁAD 1 Wprowadzenie do biotechnologii farmaceutycznej
mo3 wykladyJJ
ZARZĄDZANIE WARTOŚCIĄ PRZEDSIĘBIORSTWA Z DNIA 26 MARZEC 2011 WYKŁAD NR 3
Wyklad 2 PNOP 08 9 zaoczne
Wyklad studport 8
Kryptografia wyklad
Budownictwo Ogolne II zaoczne wyklad 13 ppoz
wyklad09
więcej podobnych podstron