Sr-06-lab-1.0: Różnice pomiędzy wersjami
Lab 06 - RMI (2) |
(Brak różnic)
|
Aktualna wersja na dzień 21:31, 7 wrz 2006
Java Remote Method Invocation — przekazywanie parametrów
Wprowadzenie
W modelu Java RMI istnieją dwa sposoby przekazywania parametrów i zwracania wartości metod (dla uproszczenia, dalej w opracowaniu będzie mowa tylko o parametrach, choć zagadnienie dotyczy również wartości zwracanych przez metody): przez referencję albo przez wartość. Przekazanie parametru przez referencję oznacza, że obiekt-parametr istnieje tylko w swojej pierwotnej lokalizacji, a odwołania do niego są zdalne, poprzez referencję do niego.
Przekazanie parametru przez wartość polega natomiast na skopiowaniu obiektu-parametru do maszyny wirtualnej, w której działa aplikacja wywołująca metodę zawierającą ten parametr. Późniejsze wywołania dokonywane są na tej kopii, czyli w lokalnej maszynie wirtualnej.
Przekazanie przez referencję
Przez referencję przekazywane są zawsze obiekty klas implementujących interfejs Remote
. Ilustruje to poniższy przykład prostego interfejsu obiektu dokonującego inkrementacji (zwiększenie wartości o 1).
public interface IncrInterface extends Remote { void Inc() throws RemoteException; }
Ponieważ interfejs IncInterface
dziedziczy z java.rmi.Remote
, więc obiekty klasy IncClass
, implementującej interfejs IncInterface
, będą przekazywane przez referencję, i odwołanie do nich w rzeczywistości będzie zdalne:
public class IncClass extends java.rmi.server.UnicastRemoteObject implements IncInterface { int i; public void Inc() throws RemoteException { i++; System.out.println ("i = " + i); } public IncClass() throws RemoteException { i = 0; } }
Przekazanie przez wartość
Poprzez wartość przekazywane są parametry niektórych klas języka Java oraz obiekty tych klas tworzonych przez użytkownika, które implementują interfejs java.io.Serializable
. Rolą interfejsu Serializable
jest umożliwienie przetworzenia dowolnego implementującego go obiektu do postaci umożliwiającej przesłanie go poprzez sieć komputerową (ang. marshalling). W odróżnieniu od klas typu Remote
, klasom typu Serializable
nie towarzyszy dodatkowa definicja interfejsu (gdyż nie są wywoływane zdalnie), przez co definiuje się je łatwiej niż klasy typu Remote
.
Trzymając się powyższego przykładu obiektu dokonującego inkrementacji, definicja klasy, której obiekty będą przekazywane przez wartość, mogłaby wyglądać następująco:
public class IncClass implements java.io.Serializable { int i; public void Inc() throws RemoteException { i++; System.out.println ("i = " + i); } public IncClass() throws RemoteException { i = 0; } }
Jak widać, klasa IncClass
nie implementuje żadnego dodatkowego interfejsu oprócz predefiniowanego java.io.Serializable
.
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 czy też uprawnień do ładowania kodu z innych maszyn wirtualnych Java. Ten ostatni przypadek szczególnie nas interesuje, 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ę znaleźć w pliku o nazwie java.policy
. Taki plik znajduje się zazwyczaj w podkatalogu jre/lib/security
katalogu głównego instalacji środowiska Java, ale najczęściej nie jest używany do uruchamiania konkretnych aplikacji, gdyż zawiera tylko zbiór domyślnych, globalnych reguł. 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:
grant { // Pelne uprawnienia dla wszystkich ladowanych zdalnie klas permission java.security.AllPermission; };
Za odczytanie i realizowanie w programie Java zdefiniowanych wcześniej zasad bezpieczeństwa odpowiadają klasy tzw. zarządców bezpieczeństwa: ogólna — SecurityManager
oraz 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:
public static void main(String args[]) { // utworzenie zarządcy bezpieczeństwa if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } ...
Wskazanie zarządcy bezpieczeństwa, gdzie powinien szukać definicji zasad bezpieczeństwa, następuje podczas uruchomienia programu. Przykładowo, komenda:
# java -Djava.security.policy=java.policy MyJavaProgram
wskazuje, że definicje zabezpieczeń (własność języka java.security.policy
) znajdują się w pliku java.policy
.
Zadanie
Należy zaprojektować i zaimplementować aplikację Java RMI, która zademonstruje różnice pomiędzy przekazywaniem parametrów przez referencję a przekazywaniem parametrów przez wartość. Obiekt przekazywany jako parametr powinien mieć przynajmniej jedną metodę wypisującą stan obiektu na ekran. Najistotniejsze jest zaobserwowanie, że
- stan obiektu przekazanego przez referencję (a więc dostępnego zdalnie) ulega ciągłym zmianom, gdyż obiekt znajduje się przez cały czas w jednej lokalizacji, w serwerze.
- obiekt przekazany przez wartość jest kopią, więc jego stan zostaje zainicjowany po stronie procesu, do którego trafia. Należy to zademonstrować.
Po której ze stron ukazują się komunikaty generowane przez metodę obiektu-parametru
- W przypadku przekazania obiektu przez referencję?
- W przypadku przekazania obiektu przez wartość?
Uwagi:
- Nie jest istotne, jaki będzie tryb przekazania parametru (wejściowy czy wyjściowy). W szczególności, wartość zwracaną przez metodę również można traktować jako parametr — jest ona przykładem parametru wyjściowego.
- W przypadku przkazania przez wartość parametrów typu
Serializable
należy uwzględnić informacje z punktu Bezpieczeństwo. Zaleca się, aby w obydwu programach (klienta i serwera) utworzyć obiekt zarządcy bezpieczeństwa.