SOP322/ćw - temat F1

Obiekty rozproszone RMI w języku Java.
RMI = Remote Method Invocation

literatura i inne materiały ...

Krótki opis technologii RMI

.....................................................................................................................

.....................................................................................................................

.....................................................................................................................

Dla obiektu RMI trzeba zdefiniować interfejs zawierający metody dostępne dla klientów, który dziedziczy z interfejsu java.rmi.Remote:
public interface OdleglyObiekt extends Remote
{
int powolnaMetoda(int skok) throws RemoteException;
}

.....................................................................................................................

Serwer musi zawierać klasę implementującą interfejs OdległyObiekt:

class OdleglaKlasa
implements OdleglyObiekt, Serializable
{
// konstruktor
OdleglaKlasa(String s) throws RemoteException
{
super();
try {
UnicastRemoteObject.exportObject(this);
// eksportujemy obiekt
// dzięki temu obiekt będzie "odległy"
// (exportObject() musi byc PRZED rebind())

Naming.rebind(s,this);
// rejestrujemy obiekt w rmiregistry

} catch(Exception e) {
System.err.println("sss: OdleglaKlasa(String); blad !!! "+e.getMessage());
}

// implementacja interfejsu OdleglyObiekt
private int licznik=0;
public int powolnaMetoda(int skok) throws RemoteException
{
for(int i=0; i<10; i++) {
licznik=licznik+skok;
System.out.println("sss: powolnaMetoda(); licznik="+licznik);
try {Thread.sleep(100);} catch(Exception e){};
}
return licznik;
}
}

.....................................................................................................................

Serwer musi wykonać następujące czynności:

  1. utworzyć obiekt - robi to w metodzie sss.main,
  2. wyeksportować go - robi to w konstruktorze OdleglaKlasa; eksportowanie obiektu powoduje że staje się on odległym obiektem (gdyby nie został wyeksportowany to odwołania klienta do tego obiektu spowodowałyby kopiowanie obiektu od serwera do klienta i obiekt stałby się lokalny !)
  3. zarejestrować w rmiregistry - robi to także w konstruktorze OdleglaKlasa.
  4. po wykonaniu tych czynności serwer nie kończy działania lecz oczekuje na klientów
public class sss
{
public static void main(String args[])
{
//System.setSecurityManager(new RMISecurityManager());
// chwilowo nie używamy SecurityManagera !

try {
OdleglyObiekt oo= new OdleglaKlasa("moj_obiekt");
// tworzymy obiekt

} catch(Exception e) {
System.err.println("sss: blad !!! "+e.getMessage());
}
}
}
Uwaga 1: Zamiast wywoływać jawnie UnicastRemoteObject.exportObject() wystarczy że OdległaKlasa dziedziczy z klasy UnicastRemoteObject, której konstruktor eksportuje obiekt (jednak wtedy OdległaKlasa nie może dziedziczyć z żadnej innej klasy ...).

Uwaga 2: Jeśli NIE dziedziczymy z UnicastRemoteObjecta to powinniśmy samodzielnie przedefiniować w OdleglaKlasa niektóre metody klasy Object: hashCode, equals, toString [w podanych przykładach tego się nie robi ...]; patrz też klasa RemoteObject.

.....................................................................................................................

Klient musi wykonać następujące czynności:

  1. uzyskać referencję do obiektu RMI
  2. wywoływać metody na rzecz obiektu RMI
  • public class kkk
    {
       public static void main(String args[])
       {
    //System.setSecurityManager(new RMISecurityManager());

    try {
    OdleglyObiekt oo = (OdleglyObiekt)Naming.lookup(
    "rmi://localhost/moj_obiekt");
    /* uzyskujemy ref do obiektu RMI
    trzeba oczywiście podać prawdziwy adres maszyny
    na której działa rmiregistry i serwer
    */

    System.out.println("kkk: oo.powolnaMetoda()="+oo.powolnaMetoda(1));
    // wywołujemy metodę obiektu RMI

    } catch(Exception e) {
    System.err.println("kkk: blad !!! "+e.getMessage());
    }
    }
    }

    .....................................................................................................................

    Klasy które muszą być dostępne (poprzez CLASSPATH) dla składników aplikacji RMI:

    Uwaga: jeśli używamy mechanizmu opisanego tutaj to nie wszystkie klasy muszą  być dostępne!

    .....................................................................................................................

    Czynności programisty podczas pisania aplikacji używającej RMI:

    (obrazek z książki Java & Corba)

    .....................................................................................................................

    Co można przekazać do i co może zwrócić metoda obiektu RMI :

    public interface OdleglyObiekt extends Remote
    {
    PrzykladowaKlasa Eksperyment1(PrzykladowaKlasa pk1, PrzykladowaKlasa pk2)
    throws RemoteException;
    /* metoda zwraca obiekt klasy PrzykladowaKlasa który może być
    obiektem odległym (wyeksportowanym) lub nie ...
    w tym drugim przypadku obiekt zostanie skopiowany od serwera do klienta

    metoda pobiera jako parametry dwa obiekty klasy PrzykładowaKlasa
    które zostaną skopiowane od klienta do serwera
    */

    }

    .....................................................................................................................

    Różnica między wyeksportowanym i nie-wyeksporowanym obiektem znajdującym się po stronie serwera:

    Zakładamy ze X jest obiektem RMI dostępnym dla klienta (np za pośrednictwem rmiregistry). Niech Y będzie obiektem RMI po stronie serwera dostępnym poprzez metodę X.drugiObiekt().
    Co się stanie gdy klient wywoła X.drugiObiekt().metoda1(); ???

    .....................................................................................................................
     

    Zadanie 90 (*)

    Uruchom przykład programu RMI rmi4.zip, a następnie:
    1. Zaprojektuj eksperyment który pokaże że jeśli 2 klientów uruchamia metodę na rzecz tego samego obiektu RMI, to są oni obsługiwani na osobnych wątkach (przerób implementację powolnaMetoda() !).
    2. Sprawdź i opisz różnicę w działaniu tego programu gdy drugiObiekt:
      • jest eksportowany (czyli jest obiektem odległym)
      • nie jest eksportowany
    3. Zbadaj jak są obsługiwane wyjątki które pojawiają się w metodach obiektu RMI; w tym celu dodaj następującą metodę do interfejsu OdległyObiekt:
          public void generujeBlad() throws RemoteException
          {
              throw new RemoteException("sztucznie wygenerowany blad ...");
          }
      i zobacz jak się ona zachowuje z pkt widzenia klienta ...

    W sprawozdaniu umieść odp na pytania, kod, wydruki produkowane przez programy.

    Wskazówki do zadania 90:
    Czynności podczas uruchamiania programu rmi4:

    1. klient i serwer mają własne katalogi ...
    2. w katalogu serwera:
      • kompilujemy sss.java poleceniem:
            javac sss.java
        powstaną pliki:
            OdleglaKlasa.class - klasa OdleglaKlasa implementująca interfejs OdleglyObiekt
            OdleglyObiekt.class - interfejs OdleglyObiekt
            sss.class
      • uruchamiamy kompilator rmic:
            rmic OdległaKlasa
        powstaną pliki:
            OdleglaKlasa_Skel.class
            OdleglaKlasa_Stub.class
      • kopiujemy kody klas: OdległyObiekt.class oraz OdległaKlasa_Stub.class do katalogu klienta
      • uruchamiamy rmiregistry; rmiregistry MUSI mieć dostęp do klas serwera poprzez zmienną środowiska CLASSPATH (zazwyczaj wystarczy uruchomić rmiregistry z katalogu serwera!)
      • uruchamiamy serwer poleceniem:
            java sss
    3. w katalogu klienta:
      • kompilujemy kod klienta
      • uruchamiamy klienta poleceniem:
            java kkk

    Zadanie 91 (*)


    ["Odległa" wersja problemu producenta & konsumenta]
    Zaprogramuj obiekt RMI "OdległyBufor" (nazwa interfejsu), rozwiązujący problem producenta i konsumenta.
    Obiekt RMI powinien mieć metody:
    void WstawElement(int el) 
    int PobierzElement()
    // jak widać do bufora wstawiamy jedynie liczby "int" !
  • Powinien istnieć pomocniczy obiekt RMI "TwórzOdległeBufory" dostępny przez nazwę "producent_konsument" w rmiregistry, zawierający metodę:

    OdleglyBufor nowyOdległyBufor()
      // mogą z niej korzystać dwa wątki tego samego procesu ...

    tworzącą obiekty OdległyBufor na żądanie.
    Przygotuj eksperyment pokazujący że to wszystko działa. Do sprawozdania wstaw kody źródłowe klas oraz wydruki eksperymentów ...

    Zadanie 91a (*)


    Do odległego bufora z poprzedniego zadania dodaj metodę:

  • OdleglyBufor nowyOdległyBuforZNazwa(String nazwa)
    
  • która pozwala korzystać z bufora dwóm różnym procesom (dostają się od tego samego buforu podając tę samą nazwę).

    .....................................................................................................................

    Dynamiczne ładowanie potrzebnych klas w RMI - java.rmi.server.codebase

    Opis dynamicznego ładowania klas z dokumentacji java.sun.com.

    Wstępne przypomnienie pewnych cech języka Java:

    Klient i serwer aplikacji RMI muszą mieć dostęp do kodu wielu klas, przykładowo:

    RMI posiada specjalny mechanizm dynamicznego ładowania potrzebnych klas przez sieć, przy czym klasy mogą być ładowane w obu kierunkach: od serwera do klienta i w przeciwnym kierunku!

    Aby mechanizm ten działał należy:

    1. serwer musi podać URL z którego mogą być ładowane klasy serwera (w szczególności pieniek); URL podaje się w właściwości java.rmi.server.codebase, np w taki sposób:
              java -Djava.rmi.server.codebase=file:///D:\Java\jdk1.2.2\Przyklad\rmi4a\SERWER\ klasa_serwera
                  // pod Windows
              java -Djava.rmi.server.codebase=file:///home/mhanckow/katalog/ klasa_serwera
                  // pod Linux-em
              java -Djava.rmi.server.codebase=http://qqq.www.eee/~jkowalski/moje_klasy klasa_serwera
    2. podobnie klient musi podać URL do swoich klas (np klas obiektów przekazywanych jako parametry metod odległego obiektu)
    3. jeżeli klient i serwer NIE pracują na tej samej maszynie i nie mają dostępu do tego samego systemu plików to trzeba się posłużyć jakimś niezależnym sposobem kopiowania plików, np serwerem httpd lub ftp (wtedy użyjemy codebase=http://...);
    4. rmiregistry MUSI być tak uruchomione żeby NIE MIAŁO(!!!) dostępu do klas serwera poprzez CLASSPATH
    5. interfejs odległego obiektu koniecznie musi mieć słowo "public" (!!!)
    6. [implementacja mechanizmu dynamicznego ładowania klas] gdy obiekt jest przesyłany przez sieć to towarzyszy mu informacja GDZIE można znaleźć kod klas tego obiektu, informację tę definiujemy właśnie w codebase ...

    .....................................................................................................................

    Zadanie 92 (*)


    [Zastosowanie dynamicznego ładowania klas]
    Do przykładu rmi4.zip dodaj zdalną metodę:

    PrzykladowaKlasa Eksperyment1(PrzykladowaKlasa pk1, PrzykladowaKlasa pk2);

    PrzykładowaKlasa powinna zawierać jakieś dane i metodę pokaz() wyświetlającą te dane; ponadto konstruktor powinien wyświetlać napis informujący o swoim uruchomieniu. Niech klient uruchamia tę metodę podając jako parametry obiekt klasy PrzykładowaKlasa, oraz obiekt podklasy PrzykładowaKlasa o nazwie PrzykładowaKlasa2. Metoda Eksperyment1() powinna uruchamiać pokaz() dla swoich parametrów, podobnie klient powinien uruchomić pokaz() dla obiektu zwróconego przez Eksperyment1().
    Klasy których potrzebują klient i serwer powinny być dynamicznie ściągane przy pomocy mechanizmu "codebase". Należy wykorzystać serwer atos i katalog html_public i w nim (w odp podkatalogach) umieścić wszystkie potrzebne klasy. Proszę spróbować uruchomić klienta i serwera na innych maszynach (np na lokalnym linixie i na atosie, ew na 2 lokalnych linuxach). Do sprawozdania proszę oprócz kodu w Javie wstawić także wydruki eksperymentów dowodzące że wszystko działa poprawnie ...

    Zadanie 93 (*)


    ["OdległyBufor" pozwalający wstawiać i pobierać dowolne obiekty]
    Rozwiąż zadanie odległy prod & kons ale tym razem zezwala się na wstawianie i pobieranie dowolnych obiektów (niekoniecznie liczb "int"); tak więc interfejs odległego obiektu powinien mieć metody:
    void WstawElement(Object el) 
    Object PobierzElement()

    Koniecznie użyj mechanizmu "codebase" do kopiowania klas. Zaprojektuj odp przykłady pokazujące że Twoje rozwiązanie działa poprawnie ...