300 likes | 435 Views
Programowanie sieciowe w Javie RMI. Wykład 11. mgr inż. Michał Misiak. 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
E N D
Programowanie sieciowe w JavieRMI Wykład 11 mgr inż. Michał Misiak
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 wspierania różnych rodzajów transportu np. HTTP.
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 Serwer i Klient RMI
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 JVM 2 JVM 1 RMI, 2 rmiregistry Klient RMI RMI, 1 RMI, 3 1 – zarejestrowanie zdalnego obiektu w usłudze nazw 2 – uzyskanie referencji do Obiektu o żądanej nazwie 3 – wywołanie metody na zdalnym obiekcie Serwer RMI
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 • każda z metod interfejsu deklaruje java.rmi.RemoteException
Zdalne wołanie metod Remote JVM Local JVM SIEĆ Klient Serwer Rozliczeniowy wypłac_pieniadze(instancja pieniadza) STUB SKELETON RMI Runtime RMI Runtime
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 executeTask(Task)
Compute & Task interface Implementacja tego interfejsu mówi, że dany komponent jest zdalny. Compute Interface wyjątek rzucany przez RMI, w np. przypadku utraty komunikacji package compute; import java.rmi.Remote; import java.rmi.RemoteException; public interface Compute extends Remote { <T> T executeTask(Task<T> t) throws RemoteException; } Task Interface package compute; public interface Task<T> { T execute(); } Uwaga! Obiekty przenoszone są po przez wartość. Generyczny typ zwracany po wykonaniu zdania.
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 rmiregistry lub innej usłudze nazw np. JNDI ComputeEngine.java
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 klasy bazowej
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 Tworzenie i instalowanie SM if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); }
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 zadeklarowanych zasobów, np. wybrany port jest już wykorzystywany. 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. Registry registry = LocateRegistry.getRegistry(); registry.rebind(name, stub);
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 ComputeEngine public interface Task<T> { T execute(); }
Interfejs Task i klasa ComputePi public interface Task<T> { T execute(); } Nazwa hostu, na którym Compute jest uruchomione 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:"); e.printStackTrace(); } } }
Mechanizm działania ComputeEngine • Uwaga! • Każda zseralizowana klasa musi deklarować • zmienną statyczną serialVersionUID w celu • zapewnienia kontroli wersji serializacji Registry.lookup rmiregistry ComputePi Registry.rebind Compute.executeTask ComputeEngine
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 miejscu
Kompilacja serwera • Kompilacja: • javac -cp c:\home\ann\public_html\classes\compute.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 Dla serwera: grant codeBase "file:/home/ann/src/" { permission java.security.AllPermission; }; Wszelkie przyzwolenia dla kodu znajdującego się w katalogu źródłowym Dla klienta: grant codeBase "file:/home/jones/src/" { permission java.security.AllPermission; };
Uruchomienie serwera i klienta • Uruchomienie rejsteru RMI • start/javaw rmiregistry Uruchomienie serwera 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 Uruchomienie klienta java -cp c:\home\jones\src;c:\home\jones\public_html\classes\compute.jar -Djava.rmi.server.codebase=file:/c:/home/jones/public_html/classes/ -Djava. security.policy=client.policy client.ComputePi zaphod.east.sun.com 45