Uniwersytet Łódzki
Wydział Matematyki i Informatyki, Katedra Analizy Nieliniowej
Programowanie sieciowe w Javie RMI
Wykład 11
Remote Method Invocation
RMI jest to API, które pozwala na zdalne wywoływanie procedur RPC (Remote Procedure Calls) w ramach JVM
JVM mogą być rozmieszczone na różnych maszynach
Aplikacje RMI składają się z dwóch elementów serwera i klienta
Aplikacje nazywane są distributed object application
RMI pozwala na dynamiczne ładowanie definicji klas
(stan + zachowanie) – możliwość zdalnego rozszerzenia funkcjonalności rozproszonych aplikacji
Początkowo RMI API zostało zaprojektowane do
Serwer i Klient RMI
Serwer:
tworzy zdalne obiekty i udostępnia na nie
referencje
oczekuje na wywołanie metod na tych
obiektach przez klienta
Klient:
uzyskuje referencje na jeden bądź więcej
obiektów zdalnych umieszczonych na serwerze
woła metody na
zdalnych obiektach
Funkcje Distributed Object Apps
Lokalizacja zdalnych obiektów.
Wiele sposobów na uzyskanie referencji np. obiekty mogą zostać zarejestrowane w usłudze nazw RMI-Registry lub referencja może zostać przekazana podczas wywoływania innej zdalnej metody.
Komunikacja ze zdalnymi obiektami
Komunikacja ze zdalnymi obiektami obsługiwana jest
bezpośrednio przez RMI. Dla programisty komunikacja RMI wygląda identycznie jak wołanie metod.
Załadowanie definicji klas
Możliwość załadowania definicji klasy lub przesłania jej po przez
sieć.
Komunikacja RMI
Klient RMI
Serwer RM
rmiregistry
RMI, 1 RMI, 2
RMI, 3
1 – zarejestrowanie zdalnego obiektu w usłudze nazw
2 – uzyskanie referencji do Obiektu o żądanej nazwie 3 – wywołanie metody na
JVM 1 JVM 2
Zdalne interfejsy obiekty i metody
Rozproszone aplikacje RMI składają się z interfejsów i klas. Klasy implementują metody z interfejsów oraz ew.
dodatkowe
Obiekty, których metody mogą być wołane po przez zewnętrzną JVM nazywają się zdalnymi obiektami (remote object)
Obiekt staje się obiektem zdalnym w wyniku
implementacji zdalnego interfejsu, a interfejs ten ma następujące cechy:
rozszerza java.rmi.Remote
Zdalne wołanie metod
Klient Serwer
Rozliczeniowy
STUB SKELETON
RMI Runtime RMI Runtime
SIEĆ
wypłac_pieniadze(instancja pieniadza)
Local JVM Remote JVM
Stub i Skeleton
Klient woła zdalną metodę, wywołanie przekazywane jest do STUB’a
STUB jest odpowiedzialny za przesłanie zdalnego
wywołania do leżącego po stronie serwera SKELETON’a
STUB otwiera gniazdo dla zdalnego serwera, serializuje parametry obiektu i przekazuje strumień danych do
SKELETON’u
SKELETON zawiera metodę, która odbiera zdalne wywołanie, odtwarza zserializowane parametry i
wywołuje rzeczywistą zdalną implementację obiektu.
Kroki przy tworzeniu aplikacji RMI
Projekt i implementacja komponentów rozproszonej aplikacji RMI
Kompilacja źródeł
Generacja Stub i Skeleton
Uczynienie klas dostępnych po przez sieć
Uruchomienie rmiregistry
Uruchomienie aplikacji
Projektowanie i implementacja komponentów
Określenie, które komponenty są lokalne, a które zdalne
Definicja zdalnych interfejsów. Określają metody, które będą mogły być wołane zdalnie przez klienta.
Implementacja zdalnych obiektów. Zdalny obiekt musi implementować jeden bądź, więcej zdalnych interfejsów.
Jeśli jakaś lokalna klasa jest przekazywana jako parametr bądź zwracana jako wartość musi zostać zaimplementowana jako zdalna.
Implementacja klienta.
Uczynienie klas dostępnych przez sieć
Jednym ze sposobów jest umieszczenie
skompilowanych klas na web serwerze.
Przykładowy projekt aplikacji RMI
Nazwa projektu Compute Engine (CE), Java Sun Tutorial
CE jest obiektem na serwerze, który otrzymuje od klienta różnego rodzaju zdania, dokonuje ich przeliczenia i zwraca wynik
CE charakteryzuje się dużą elastycznością realizowanych zdań i może być wykorzystywany przez wielu klientów
Oczywiście zbiór zdań nie musi być predefiniowany w momencie uruchamiania, czy też pisania silnika
Wymaganie! Klasa reprezentująca zadanie musi implementować predefiniowane interfejsy.
Aplikacja CE nie musi mieć świadomości zawartości zadania, klasy
ładowane są dynamicznie – takie aplikacje nazywają się behavior-
based application
Projekt zdalnego interfejsu
Głównym element służącym komunikacji
pomiędzy klientem, a serwerem jest protokół, który reprezentowany jest przez zdalny interfejs
Compute Interfece pozwala na wykonywanie metod przez CE. Implementując interfejsu
java.rmi.Remote wskazuje na to, że jest zdalny.
Task Interface mówi o tym jak powinno zostać
dane zadanie wykonane. Jest parametrem, który
jest umieszczany w metodzie
Compute & Task interface
package compute;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Compute extends Remote {
<T> T executeTask(Task<T> t) throws RemoteException;
}
Compute Interface
Task Interface
package compute;
public interface Task<T> { T execute();
wyjątek rzucany przez RMI, w np. przypadku utraty komunikacji Implementacja tego interfejsu mówi,
że dany komponent jest zdalny.
Implementacja zdalnych interfejsów
Klasa reprezentująca zdalny obiekt powinna:
Deklarować zdalne interfejsy
Definiować konstruktor dla każdego zdalnego obiektu
Implementować metody zdalnego interfejsu
Inicjalizacja zdalnego obiektu i eksport do RMI runtime w ramach RMI serwera
utworzenie i instalacja security manager
utworzenie i eksport zdalnych obiektów
Rejestracja przynajmniej jednego obiektu w
Deklaracja zdalnego interfejsu i jego implementacja
Deklaracja implementacji zdalnego interfejsu:
public class ComputeEngine implements Compute
Konstruktor ComputeEngine oraz metoda main mogą być wołane jedynie lokalnie.
Metoda main tworzy instancje ComputeEngine i czyni ją dostępną dla klienta
Konstruktor woła bezparametrowo konstruktor
Implementacja zdalnych metod
public <T> T executeTask(Task<T> t) { return t.execute();
}
Implementacja metody executeTask tworzy swego rodzaj protokół pomiędzy klientem a serwerem.
Parametr typu Task musi implementować interfejs, który posiada metodę execute.
Umieszczanie obiektów w RMI
Każdy typ argumentu lub wartości zwracanej może być przekazywany w sytuacji, gdy jest: prymitywnym typem, zdalnym obiektem, typem, który może być zserializowany
Obiekty typów takich jak: wątki, deskryptory pliku – nie mogą być serializowane
Dana klasa jest serializowana, wówczas, gdy implementuje interfejs Serializable
Obiekty zdalne przekazywane są przez referencje. Referencja na zdalny obiekt to STUB.
STUB – proxy po stronie klienta, który implementuje pełen zbiór interfejsów zdalnych, implementowanych przez obiekt zdalny
Obiekty lokalne przekazywane są jako kopie, wykorzystując serializację.
Wszystkie pola są kopiowane z wyjątek transient i static.
W przypadku przekazywania obiektu zdalnego, dostępne są wyłącznie interfejsy zdalne dla zdalnego JVM. Metody w ramach interfejsów nie-zdalnych nie będą widoczne dla zdalnego JVM.
Implementacja metody main
Wykorzystywana do uruchomienia
ComputeEngine – musi zainicjalizować wybrane komponenty:
Utworzenie i instalacja Security Manager
Uczynienie obiektów zdalnych widocznych dla
klienta
Utworzenie i instalacja Security Manager
Chroni system przez niezaufanymi źródłami
Określa prawa dostępu do zasobów:
wykonywanie określonych poleceń, korzystanie z lokalnego systemu plików
Niezainstalowanie SM uniemożliwi ściągnięcie
klas dla parametrów przekazywanych w zdalnym
wywołaniu lub zwracanych
Udostępnianie zdalnych obiektów klientowi
UnicastRemoteObject.exportObject –
metoda eksportuje dostarczone zdalne obiekty, tutaj jest to engine
drugi parametr to port TCP. Wartość 0 oznacza port anonimowy – port jest wybierany przez system, bądź przy starcie RMI
poprawne wykonanie metody exportObject pozwala na wykonywanie zdalnych wywołań przez ComputeEngine
typ na który jest rzutowanie to Compute, gdyż Compute jest interfejsem zdalnym
metoda exportObject może rzucić wyjątkiem RemoteException
Wyjątek RemoteException jest rzucany w przypadku braku
Compute engine = new ComputeEngine();
Compute stub = (Compute) UnicastRemoteObject.exportObject(engine, 0);
rmiregistry
Jest to szczególny typ obiektu zdalnego, który służy do
znajdowywania referencji na inne zdalne obiekty po przez nazwę
rmiregistry służy przeważnie do znajdowania pierwszego
zdalnego obiektu, za pomocą którego będą uzyskane referencje na inne zdalne obiekty
Wywołanie metody getRegistry bez parametrów powoduje założenie rejestru na lokalnej maszynie na porcie 1099
Utworzony obiekt ComputeEngine nie jest nigdy przekazywany.
Następuje przekazanie wyłączenie STUB w wyniku wyszukania danego obiektu zdalnego w rejestrze nazw
Nie jest konieczne utrzymywanie przy życiu obiektu
ComputeEngine. Dopóki istnieje referencja na ComputeEngine w
innej JVM dopóty serwer nie zostanie wyłączony.
Program klienta
Na kliencie wymagana jest definicja
zadania, które zostanie wykonane zdalnie
Klasa ComputePi wyszukuje i wywołuje obiekt Compute
Klasa Pi implementuje interfejs zadania (Task Interface) oraz definiuje pracę,
która powinna zostać wykonana przez
Interfejs Task i klasa ComputePi
public interface Task<T> { T execute();
}
public class ComputePi {
public static void main(String args[]) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager()); } try { String name = "Compute";
Registry registry = LocateRegistry.getRegistry(args[0]);
Compute comp = (Compute) registry.lookup(name);
Pi task = new Pi(Integer.parseInt(args[1]));
BigDecimal pi = comp.executeTask(task);
System.out.println(pi);
} catch (Exception e) {
System.err.println("ComputePi exception:");
Nazwa hostu, na którym Compute jest uruchomione
Mechanizm działania ComputeEngine
ComputePi
rmiregistry
ComputeEngine
Registry.lookup
Compute.executeTask
Registry.rebind
Uwaga!
•Każda zseralizowana klasa musi deklarować zmienną statyczną serialVersionUID w celu zapewnienia kontroli wersji serializacji
Kompilacja interfejsów
Projekt został podzielony na 3 pakiety:
compute – zawiera interfejsy Compute and Task
engine – implementacja klasy ComputeEngine
client – zawiera kod klienta ComputePi oraz implementacje zadania Pi
Stworzenie JAR z interfejsów
javac compute\Compute.java
compute\Task.java jar cvf compute.jar compute\*.class
Umieszczenie plików w ogólnie dostępnym z sieci
Kompilacja serwera
Kompilacja:
javac -cp
c:\home\ann\public_html\classes\co mpute.jar
engine\ComputeEngine.java
Kompilacja klienta
Kompilacja:
javac -cp
c:\home\jones\public_html\classes\
compute.jar client\ComputePi.java client\Pi.java
Klasę Pi należy ponownie skompilować
ponieważ ma być dostępna dla serwera.
Specyfikacja Security Manager
grant codeBase "file:/home/ann/src/" {
permission java.security.AllPermission;
};
Dla serwera:
Dla klienta: grant codeBase "file:/home/jones/src/" { permission java.security.AllPermission;
};
Wszelkie przyzwolenia dla kodu znajdującego się w katalogu źródłowym
Uruchomienie serwera i klienta
Uruchomienie rejsteru RMI
start/javaw rmiregistry
java -cp c:\home\ann\src;c:\home\ann\public_html\classes\compute.jar -Djava.rmi.server.
codebase=file:/c:/home/ann/public_html/classes/compute.jar -Djava.rmi.server.
hostname=zaphod.east.sun.com -Djava.security.policy=server.policy engine.ComputeEngine
java -cp c:\home\jones\src;c:\home\jones\public_html\classes\compute.jar
Uruchomienie serwera
Uruchomienie klienta