Personal tools

Sr-06-lab-1.0

From Studia Informatyczne

Spis treści

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

  1. W przypadku przekazania obiektu przez referencję?
  2. W przypadku przekazania obiektu przez wartość?

Uwagi:

  1. 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.
  2. 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.