RMI - Remote Method Invocation
RMI - Remote Method Invocation
RMI - Remote Method Invocation
RMI (Java Remote Method Invocation) es un mecanismo ofrecido por Java para invocar un mtodo de manera remota. Forma parte del entorno estndar de ejecucin de Java y proporciona un mecanismo simple para la comunicacin de servidores en aplicaciones distribuidas basadas exclusivamente en Java. Si se requiere comunicacin entre otras tecnologas debe utilizarse CORBA o SOAP en lugar de RMI. RMI se caracteriza por la facilidad de su uso en la programacin por estar especficamente diseado para Java; proporciona paso de objetos por referencia (no permitido por SOAP), recoleccin de basura distribuida (Garbage Collector distribuido) y paso de tipos arbitrarios (funcionalidad no provista por CORBA). A travs de RMI, un programa Java puede exportar un objeto, con lo que dicho objeto estar accesible a travs de la red y el programa permanece a la espera de peticiones en un puerto TCP. A partir de ese momento, un cliente puede conectarse e invocar los mtodos proporcionados por el objeto. La invocacin se compone de los siguientes pasos: Encapsulado (marshalling) de los parmetros (utilizando la funcionalidad de serializacin de Java). Invocacin del mtodo (del cliente sobre el servidor). El invocador se queda esperando una respuesta. Al terminar la ejecucin, el servidor serializa el valor de retorno (si lo hay) y lo enva al cliente. El cdigo cliente recibe la respuesta y contina como si la invocacin hubiera sido local.
ARQUITECTURA
La arquitectura RMI puede verse como un modelo de cuatro capas. Un stub es un trozo de cdigo usado como sustituto de alguna otra funcionalidad. Un stub puede simular el comportamiento de cdigo existente o ser el sustituto temporal para un cdigo an no desarrollado. Estos Stubs y Skeletons permiten que al momento de ser invocada la funcin remota esta pueda ser "simulada localmente"
Primera capa
La primera capa es la de aplicacin y se corresponde con la implementacin real de las aplicaciones cliente y servidor. Aqu tienen lugar las llamadas a alto nivel para acceder y exportar objetos remotos. Cualquier aplicacin que quiera que sus mtodos estn disponibles para su acceso por clientes remotos debe declarar dichos mtodos en una interfaz que extienda java.rmi.Remote. Dicha interfaz se usa bsicamente para "marcar" un objeto como remotamente accesible. Una vez que los mtodos han sido implementados, el objeto debe ser exportado. Esto puede hacerse de forma implcita si el objeto extiende la clase UnicastRemoteObject (paquete java.rmi.server), o puede hacerse de forma explcita con una llamada al mtodo exportObject() del mismo paquete
Segunda capa
La capa 2 es la capa proxy, o capa stub-skeleton. Esta capa es la que interacta directamente con la capa de aplicacin. Todas las llamadas a objetos remotos y acciones junto con sus parmetros y retorno de objetos tienen lugar en esta capa.
Tercera capa
La capa 3 es la de referencia remota, y es responsable del manejo de la parte semntica de las invocaciones remotas. Tambin es responsable de la gestin de la replicacin de objetos y realizacin de tareas especficas de la implementacin con los objetos remotos, como el establecimiento de las persistencias semnticas y estrategias adecuadas para la recuperacin de conexiones perdidas. En esta capa se espera una conexin de tipo stream (stream-oriented connection) desde la capa de transporte.
Cuarta Capa
La capa 4 es la de transporte. Es la responsable de realizar las conexiones necesarias y manejo del transporte de los datos de una mquina a otra. El protocolo de transporte subyacente para RMI es JRMP (Java Remote Method Protocol), que solamente es "comprendido" por programas Java.
Elementos Toda aplicacin RMI normalmente se descompone en 2 partes: Un servidor, que crea algunos objetos remotos, crea referencias para hacerlos accesibles, y espera a que el cliente los invoque. Un cliente, que obtiene una referencia a objetos remotos en el servidor, y los invoca.
Ejemplo
Un servidor RMI consiste en definir un objeto remoto que va a ser utilizado por los clientes. Para crear un objeto remoto, se define una interfaz, y el objeto remoto ser una clase que implemente dicha interfaz. Pasos para crear el servidor RMI Definir la interfaz remota. Cuando se crea una interfaz remota: La interfaz debe ser pblica. Debe heredar de la interfaz java.rmi.Remote, para indicar que puede llamarse desde cualquier mquina virtual Java. Cada mtodo remoto debe lanzar la excepcin java.rmi.RemoteException en su clusula throws, adems de las excepciones que pueda manejar.
public interface MiInterfazRemota extends java.rmi.Remote { public void miMetodo1() throws java.rmi.RemoteException; public int miMetodo2() throws java.rmi.RemoteException; }
public class MiClaseRemota extends java.rmi.server.UnicastRemoteObject implements MiInterfazRemota { public MiClaseRemota() throws java.rmi.RemoteException {
// Cdigo del constructor } public void miMetodo1() throws java.rmi.RemoteException { // Aqu ponemos el cdigo que queramos System.out.println("Estoy en miMetodo1()"); } public int miMetodo2() throws java.rmi.RemoteException { return 5; // Aqu ponemos el cdigo que queramos } public void otroMetodo() { // Si definimos otro mtodo, ste no podra llamarse // remotamente al no ser de la interfaz remota } public static void main(String[] args) { try { MiInterfazRemota mir = new MiClaseRemota(); java.rmi.Naming.rebind("//" + java.net.InetAddress.getLocalHost().getHostAddress() + ":" + args[0] + "/PruebaRMI", mir); } catch (Exception e) { } } } Como se puede observar, la clase MiClaseRemota implementa la interfaz MiInterfazRemota que hemos definido previamente. Adems, hereda de UnicastRemoteObject, que es una clase de Java que podemos utilizar como superclase para implementar objetos remotos. Luego, dentro de la clase, definimos un constructor (que lanza la excepcin RemoteException porque tambin la lanza la superclase UnicastRemoteObject), y los mtodos de la/las interfaz/interfaces que implemente. Finalmente, en el mtodo main, definimos el cdigo para crear el objeto remoto que se quiere compartir y hacer el objeto remoto visible para los clientes, mediante la clase Naming y su mtodo rebind(...).
Nota: Hemos puesto el mtodo main() dentro de la misma clase por comodidad. Podra definirse otra clase aparte que fuera la encargada de registrar el objeto remoto. Compilar y ejecutar el servidor
Ya tenemos definido el servidor. Ahora tenemos que compilar sus clases mediante los siguientes pasos: Compilamos la interfaz remota. Adems lo agrupamos en un fichero JAR para tenerlo presente tanto en el cliente como en el servidor: javac MiInterfazRemota.java jar cvf objRemotos.jar MiInterfazRemota.class Luego, compilamos las clases que implementen las interfaces. Y para cada una de ellas generamos los ficheros Stub y Skeleton para mantener la referencia con el objeto remoto, mediante el comando rmic: set CLASSPATH=%CLASSPATH%:.\objRemotos.jar:. javac MiClaseRemota.java rmic -d . MiClaseRemota Observamos en nuestro directorio de trabajo que se han generado automticamente dos ficheros.class (MiClaseRemota_Skel.class y MiClaseRemota_Stub.class) correspondientes a la capa stub-skeleton de la arquitectura RMI. Para ejecutar el servidor, seguimos los siguientes pasos: Se arranca el registro de RMI para permitir registrar y buscar objetos remotos. El registro se encarga de gestionar un conjunto de objetos remotos a compartir, y buscarlos ante las peticiones de los clientes. Se ejecuta con la aplicacin rmiregistry distribuida con Java, a la que le podemos pasar opcionalmente el puerto por el que conectar (por defecto, el 1099). En el caso de Windows, se debe ejecutar: start rmiregistry 1234
Cliente
Crear un cliente RMI
Vamos ahora a definir un cliente que acceder a los objetos remotos que creemos. Para ello seguimos los siguientes pasos: Definir la clase para obtener los objetos remotos necesarios
La siguiente clase obtiene un objeto de tipo MiInterfazRemota, implementado en nuestro servidor: public class MiClienteRMI { private MiClienteRMI(){}; public static void main(String[] args) { try { MiInterfazRemota mir = (MiInterfazRemota)java.rmi.Naming.lookup("//" + args[0] + ":" + args[1] + "/PruebaRMI"); // Imprimimos miMetodo1() tantas veces como devuelva miMetodo2() for (int i=1;i<=mir.miMetodo2();i++) mir.miMetodo1(); } catch (Exception e) { e.printStackTrace(); } } }
Como se puede observar, simplemente consiste en buscar el objeto remoto en el registro RMI de la mquina remota. Para ello usamos la clase Naming y su mtodo lookup(...). Compilar y ejecutar el cliente
Una vez que ya tenemos definido el cliente, para compilarlo hacemos: set CLASSPATH=%CLASSPATH%;.\objRemotos.jar;. javac MiClienteRMI.java Luego, para ejecutar el cliente hacemos: java MiClienteRMI 127.0.0.1 1234