Java remote method invocation
Java remote method invocation (Java RMI) je technologie programovacího jazyka Java umožňující z jednoho virtuálního stroje (JVM) volat metody objektů na jiném virtuálním stroji, který obvykle běží na jiném počítači (hostu). Výhodou RMI je, že programátor zachazí se vzdáleným objektem, jako by byl místní. Rozhraní a třídy, které jsou zodpovědné za funkčnost RMI jsou nadefinovány v balíčku java.rmi. RMI se poprvé objevilo v JDK 1.1 a využívalo protokol JRMP. Později byla uvedena nová verze RMI, RMI-IIOP, aby RMI bylo kompatibilní s platformně nezávislým systémem CORBA.
Architektura
RMI se skládá ze tří vrstev. Stub na straně klienta a Skeleton na straně serveru, vrstvy Remote reference a vrstvy Transport.
Stub je vrstvou mezi aplikací klientu a remote reference a je jakýsi zástupce vzdáleného objektu. Když je na straně klienta zavolána metoda vzdáleného objektu, tak stub vytváří blok informací, který obsahuje identifikátor vzdáleného objektu; číslo metody, která se má zavolat; parametry zabalené do marshall streamu, tj zabalené tak, aby se daly posílat po síti.
Skeleton je protějšek stubu na straně serveru. Má na starost rozbalení masrshall streamů; vyvolání metody skutečného objektu; zachycení návratové hodnoty nebo výjimky metody, která byla zavolána a zabalení této hodnoty / výjimky do marshall streamu; Od Java 2 SDK není skeleton vyžadován.Pro generování stub a skeleton se používá RMI překladač, rmic.
Pokud metoda, která je na vzdáleném objektu zavolána vrací nějakou hodnotu, nebo vyhodí výjimku, tak stub se postará opětovně o její rozbalení a vrácení / vyvolání na straně klienta.
Vrstva remote reference je prostředník mezi stub a skeleton.
Vrstva transport vytváří a spravuje spojení mezi dvěma JVM. Toto spojení využívá protokol TCP/IP. Nad protokolem TCP/IP RMI využívá protokol Java Remote Method Protokol (JRMP). Od verze 1.3 Java 2 SDK je k dispozici další verze RMI zvaná RMI/IIOP, která namísto JRMP používá Internet Inter-ORB Protocol (IIOP).
Získávání vzdálených objektů
Aby mohl klient využívat metody vzdáleného objektu, je nutné, aby na tento objekt měl referenci, kterou musí server poskytnout. Pro získávání a poskytování objektů se využívá RMI registry, kam server pomocí třídy java.rmi.Naming zanáší stub pro daný objekt a umožní klientu si tento stub z RMI registry s pomocí třídy java.rmi.Naming stáhnout. Referenci lze získat i v návratové hodnotě metody, nicméně minimálně první stub se musí stáhnout přes RMI registry.
Posílání Parametrů
V RMI lze jako parametr či návratovou hodnotu posílat veškeré primitivní typy, vzdálené objekty a objekty, které implementují rozhraní java.io.Serializable. Posílání objektů, které nejsou vzdálené, se řeší jejich serializováním a posláním ve streamu. Pokud tedy server vrátí klientu jako návratovou hodnotu tento typ objektu, je na straně klientu vytvořen nový objekt. Naproti tomu pokud je vrácenou hodnotou vzdálený objekt, předá se klientovi stub.
Distribuce RMI tříd
RMI podporuje dynamické stahování tříd. To znamená že pokud například klient dostane jako návratovou hodnotu objekt třídy, pro kterou nemá potřebnou definici, je schopen si ji stáhnout ze vzdáleného FTP nebo HTTP serveru. Potřebné URL ukazující na FTP/HTTP server je posláno společně s daným objektem.
Příklad použití
Třída, jejíž objekty chceme vzdáleně volat, se skládá z rozhraní a vlastní implementace.
Rozhraní:
Rozhraní definuje metody, které může klient volat. Musí být public; musí být potomek java.rmi.Remote a u každé metody musí být uvedeno, že vyhazuje java.rmi.RemoteException.
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloIntf extends Remote {
public String rekniHello() throws RemoteException;
}
Implementace hello:
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Hello extends UnicastRemoteObject implements HelloIntf {
public Hello() throws RemoteException {
}
public String rekniHello() throws RemoteException {
return "Hello World!";
}
}
Server:
import java.rmi.Naming;
public class Server {
public static void main(String[] args) {
try {
Naming.rebind("Hello", new Hello());
} catch (Exception e) {
}
}
}
Klient:
import java.rmi.Naming;
public class Klient {
public static void main(String[] args) {
try {
HelloIntf hello = (HelloIntf)Naming.lookup("/localhost/Hello");
System.out.println(hello.rekniHello());
} catch (Exception e) {
}
}
}