Tworzenie i wykorzystanie obiektów z podklasy UnicastRemoteObject

Plik Matematyka.java

Plik ten zawiera deklarację interfejsu, jaki będzie udostępniany odległym klientom. Ci klienci będą mogli jedynie odwoływać się do metod zadeklarowanych w tym interfejsie.

import java.rmi.*;

public interface Matematyka extends Remote {
    int fib(int t) throws RemoteException;
    int ack(int m, int n) throws RemoteException;
}

Plik MatematykaImpl.java

Plik ten zawiera deklarację klasy implementującej interfejs Matematyka. Obiekt tej klasy będzie służył jako serwer udostępniający zdalne usługi. Przykład ten jest rozszerzony o kilka pomocniczych metod raportujących stan serwera: wywołanie i zakończenie metod czy liczba uruchomionych wątków.

Ostatnia, statyczna metoda jest metodą służącą do utowrzenia i zarejestrowania utworzonego obiektu.


import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;

public class MatematykaImpl extends UnicastRemoteObject implements Matematyka {
    public MatematykaImpl() throws RemoteException {
        super();
        System.out.println("Nowy obiekt MatematykaImpl");
        report();
    }


    private void report(){
        System.out.println("Liczba aktywnych watkow: " + Thread.activeCount());
        System.out.println("nazwa watku: " + Thread.currentThread().getName());
    }


    private int _fib(int n){
        if (n < 2) return 1;
        else return _fib(n-1) + _fib(n-2);
    }

    public int fib(int n){
        int res;
        System.out.println("Poczatek fib");
        report();
        res = _fib(n);
        System.out.println("Koniec fib");
        return res;
    }

    private int _ack(int n, int m){
        if (n == 0) return m + 1;
        else 
            if(m == 0) return _ack(n - 1, 1);
            else return _ack(n - 1, _ack(n, m - 1));
            }

    public int ack(int n, int m){
        int res;
        System.out.println("Poczatek ack");
        report();
        res = _ack(n,m);
        System.out.println("Koniec ack");
        return res;
    }

    public static void main(String arg[]){
        //Utworzenie managera bezpieczenstwa
        if (System.getSecurityManager() == null) 
            System.setSecurityManager(new RMISecurityManager());
        try {
            MatematykaImpl obj = new MatematykaImpl();
            Naming.rebind("//127.0.0.1/Wyklad", obj);
            System.out.println("Usluga zarejestrowana");
        } catch (Exception e) {
            System.out.println("Usluga niezarejestrowana: " + e.getMessage());
        }
    }
}

Klient.java

Plik zawiera deklarację klienta korzystającego ze zdalnych usług. Konieczne jest tu wskazanie serwera, na którym uruchomiony jest program rmiregistry. Zwykle jest to ten sam serwer, na którym uruchomiony jest serwer usługowy.


import java.rmi.*;

public class Klient {

    public static void main(String arg[]){
        String server = "swiatowit";
        Matematyka m = null;

        try {
            m = (Matematyka)Naming.lookup("//" + server + "/Wyklad");
            System.out.println("Zdalny obiekt został odnaleziony");

            // wywołania zdalnych metod
            System.out.println("fib 19: " + m.fib(19));
            System.out.println("fib 20: " + m.fib(20));
            System.out.println("fib 21: " + m.fib(21));
            System.out.println("ack(3,3): " + m.ack(3,3));

        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}


Jak uruchomić środowisko

Po stronie serwera

Po stronie serwera powinne być dwa pliki: Matematyka.java i MatematykaImpl.java. Kompilowane są standardowo:

javac *.java
Następnie należy zbudować szkielet i przedstawiciela poleceniem
rmic MatematykaImpl
Tworzone są wtedy dwa pliki: MatematykaImpl_Skel.class oraz MatematykaImpl_Stub.class. Kolejny krok to uruchomienie brokera RMI:
rmiregistry &
i zarejestrowanie w nim usługi
java -Djava.security.policy=test.policy MatematykaImpl 
Plik test.policy zawiera listę przywilejów, jakie nadajemy programom wykonywanym za pomocą tego wywołania maszyny wirtualnej. Jest on konieczny, gdyż domyślne prawa są zbyt restrykcyjne, aby zarejestrować usługę. Poniżej znajduje się przykładowy plik zezwalający na wszelkie operacje, dlatego w bardziej zaawansowanych aplikacjach wymagałby modyfikacji:
grant {
  permission java.security.AllPermission;
};

Po stronie klienta

Wymagane są dwa pliki źródłowe: Klient.java i Matematyka.java (ten sam, jak po stronie serwera). Konieczne jest też skopiowanie z serwera do klienta plik MatematykaImpl_Stub.class. Następnie uruchamiany jest klient poleceniem

java Klient
Niekiedy potrzebne jest (w zależności od lokalnych ustawień) wskazanie pliku z prawami, podobnie jak w przypadku uruchomienia serwera. Plik test.policy z serwera powinien załatwić sprawę.

Serwer typu Activatable

W przypadku tego typu serwerów procedura po stronie klienta wygląda tak samo. Trochę inaczej wygląda serwer (konstruktor) oraz procedura rejestracji.

Plik MatematykaImpl.java

import java.rmi.*;
import java.rmi.activation.*;

import java.util.Properties;

public class MatematykaImpl extends Activatable implements Matematyka {

    // konstruktor
    // called by the method ActivationInstantiator.newInstance during
    // activation, to construct the object.
    //
    public MatematykaImpl(ActivationID id, MarshalledObject data) 
        throws RemoteException {
        super(id, 0);
        System.out.println("Nowy obiekt MatematykaImpl");
        report();
    }
    ...
}

Powyższy tekst zawiera jedynie deklarację klasy oraz konstruktor. Metody implementujące interfejs wyglądają identycznie jak powyżej. Inaczej natomiast wygląda metoda rejestracji obiektu:

  
    public static void main(String arg[]) throws Exception {
        // Utworzenie managera bezpieczenstwa
        if (System.getSecurityManager() == null) 
            System.setSecurityManager(new RMISecurityManager());

        // Wskazanie pliku z prawami dla danej grupy
        Properties props = new Properties(); 
        props.put("java.security.policy", "/home/..../test.policy");

        ActivationGroupDesc.CommandEnvironment ace = null; 
        ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace);
 
        // Rejestracja grupy i uzskanie identyfikatora
        ActivationGroupID agi = 
           ActivationGroup.getSystem().registerGroup(exampleGroup);

        // Wskazanie katalogu zawierającego plik class z implementacją
        // serwera
        String location = "file:/home/.../activatable/";

        // Argument dla konstruktora serwera
        MarshalledObject data = null;

        // Utorzenie środowiska dla pojedynczego obiektu
        ActivationDesc desc = new ActivationDesc
            (agi, "MatematykaImpl", location, data);

        // rejestracja w serwerze rmid
        Matematyka m = (Matematyka)Activatable.register(desc);
        System.out.println("Got the stub for the Matematyka");
        
        // rejestracja w serwerze rmiregistry &
        try {
            System.out.println("Location: " + desc.getLocation());
            Naming.rebind("Wyklad", m);
            System.out.println("Usluga zarejestrowana");
        } catch (Exception e) {
            System.out.println("Usluga niezarejestrowana: " + e.getMessage());
        }

        System.exit(0);
    }
Oprócz uruchomienia serwera rmiregistry konieczne jest też uruchomienie serwera rmid:
rmid -J-Djava.security.policy=rmid.policy &
W tym momencie można uruchomić rejesrację usługi, dokładnie tak jak poprzednio. Plik z prawami jest tu konieczny i może wyglądać następująco:
grant {
    permission com.sun.rmi.rmid.ExecPermission
        "/usr/local/java1.3.02/bin/java";

    permission com.sun.rmi.rmid.ExecPermission
        "/usr/local/java1.3.02/bin/java_g";

    permission com.sun.rmi.rmid.ExecPermission
        "/files/apps/rmidcmds/*";

    permission com.sun.rmi.rmid.ExecOptionPermission
        "-Djava.security.policy=/home/.../test.policy";
    permission com.sun.rmi.rmid.ExecOptionPermission
        "-Djava.security.debug=*";

    permission com.sun.rmi.rmid.ExecOptionPermission
        "-Dsun.rmi.*";
};

Trzeba pamiętać o ustawieniu odpowiednich ścieżek do plików wykonywalnych środowiska JAVA.