Texto íntegro de Carlos Morales,
is06200@salleurl.edu
Java Remote Method Invocation (RMI)
-

La utilidad del RMI es poder ejecutar funciones en otro ordenador , interesante si queremos por ejemplo hacer cálculos dentro de un programa y nuestro cliente no es lo suficiente potente, pues mediante RMI accederíamos a los objetos necesarios que residirían en un ordenador más potente y todo como si estuviesen nuestro propio ordenador.

La interfaz RMI declara cuales de estos métodos son accesibles desde otras Java Virtual Machines (JVM). Necesitamos crear el siguiente escenario para poder trabajar con RMI: Un interfaz que indique qué funciones son accesibles, un objeto que implemente dichas funciones, un servidor que dé de alta a la interfaz y un cliente que acceda a la interfaz remotamente.

Existen varias reglas que deben implementarse para funcionar correctamente:

La interfaz remota deben declararase "public"
El cliente debe poder cargar el objeto remoto que implementa la interfaz remota, solo puede accederse si es public.
La interfaz remota debe extender la interfaz java.rmi.Remote
Al definir la interfaz esta debe terminar con un extends java.rmi.Remote, pues debe implementar a la interfaz Remote.
Los métodos deben declarar java.rmi.RemoteException
En cada método que se declare en la interfaz se añade throws java.rmi.RemoteException junto con las excepciones que queramos.
Tipos de valores serializables.
Todos los tipos que se pasan como argumento o se retornan deben ser Serializables o heredar de clases seriablizables.

Interficie y su implementación

En la interficie solamente se declaran las funciones que pueden usarse remotamente y en el cliente se implementan.

Interfaz ejemplo: HelloInt.java

	/// Interfaz del programa HelloWorld mediante RMI
	// Carlos Morales, 2002
	package rmihello;
	
	import java.rmi.Remote;
	import java.rmi.RemoteException;
	
	public interface HelloInt extends Remote{
	
		String sayHello() throws RemoteException;
	
	}
	
Comentarios:
public interface HelloInt extends Remote{
Este objeto no es una clase es una interfaz que extiende a la interfaz Remote.
String sayHello() throws RemoteException;
Función que retorna un String y que lanza una Excepcion Remota en caso de fallar la conexión.

Implementación ejemplo: HelloImpl.java

	/// Implementación del programa HelloWorld mediante RMI
	// Carlos Morales, 2002
	package rmihello;

	import java.rmi.Remote;
	import java.rmi.RemoteException;
	import java.rmi.server.UnicastRemoteObject;

	public class HelloImpl extends UnicastRemoteObject implements HelloInt{

		public HelloImpl() throws RemoteException{
			super(); // implicit
		}
	
		public String sayHello() throws RemoteException{
			return "Hello World";
		}
	}
	
Comentarios:
public class HelloImpl ...
En este caso se trata de una clase.
...HelloImpl extends UnicastRemoteObject...
Como conveniencia puede extender una clase remota, en este ejemplo la java.rmi.server.UnicastRemoteObject.
...implements HelloInt
Implementa la interfaz HelloInt antes declarada, que promete implementar o definir cada método declarado en la interfaz.
public HelloImpl() throws RemoteException{
El constructor debe lanzar la RemoteExcepcion, que se lanzará en caso de que falle la comunicación RMI en la construcción.
public String sayHello() throws RemoteException{
Los argumentos o valores pasados por retorno pueden ser de cualquier tipo mientras implementen la interficie java.io.Serializable

El servidor

El servidor será el encargado de dar de alta al programa. Este necesita:

1. Crear e instalar un security manager
2. Crear una o más instancias al objeto remoto
3. Registrar al menos uno de los objetos remotos con el remote object registry.

Servidor ejemplo: HelloServer.java

	/// El Servidor de la aplicación HelloWorld mediante RMI
	// Carlos Morales, 2002

	package rmihello;
	
	import java.rmi.RMISecurityManager;
	import java.rmi.Naming;
	
	
	public class HelloServer{
	
		public static void main(String arg[]){
			/// 1. Crear e instalar un controlador de securidad (security manager)
			if(System.getSecurityManager()==null){
				System.setSecurityManager(new RMISecurityManager());
			}
			try{
				/// 2. Crear una o mas instancias al objeto remoto
				HelloImpl remObj = new HelloImpl();
				
				/// 3. Registrar al menos uno de los objetos remotos
				//   Ligar el objeto con el nombre "HelloServerBind"
				Naming.rebind("//MiHost/HelloServerBind",remObj);
				
				System.out.println("HelloServerBind dado de alta en el rmiregistry");
			}catch( Exception e){
				System.out.println(e.toString());
				
			}
		}
	}
	
Comentarios:
if(System.getSecurityManager()==null){
Comprobar si ya existe un SecurityManager en el sistema. Si no existe crear uno propio.
System.setSecurityManager(new RMISecurityManager())
Crear el securityManager. Necesita estar ejecutandose para garantizar que las operaciones que se ejecutan son operaciones permitidas. Si no hubiese ningún security manager no se permitiría cargar ninguna clase por los clientes RMI o por los servidores.
HelloImpl remObj = new HelloImpl();
Creamos el objeto de la clase HelloImpl.
Naming.rebind("//myhost/HelloServerBind",remObj)
Liga el nombre HelloServerBind con el objeto remoto, esta es la parte del servidor que da de alta al objeto en el registro RMI (rmiregistry) del servidor myhost, que puede ser un nombre válido o bien la IP del servidor, todo seguido del número de puerto desde el que se escucha, por defecto si no se escribe ninguno escucha en el puerto 1099. El segundo parámetro remObj es la referencia al objeto implementación.
Se debe importar la clase java.rmi.Naming para dar de alta al objeto.

El Cliente

El cliente debe "bajarse" la interfaz del servidor y quien le diga al servidor que operaciones debe ejecutar. Para ello se necesita que:

1. Crear e instalar un security manager.
2. Bajarse la interfaz mediante RMI.
3. Usar las funciones de la interfaz.

Cliente ejemplo: HelloClientRMI.java

	
	/// El Cliente de la aplicación HelloWorld mediante RMI
	// Carlos Morales, 2002
	package rmihello;
	
	import java.rmi.RemoteException;
	import java.rmi.RMISecurityManager;
	import java.rmi.Naming;
	
	public class HelloClientRMI{
	
		public static void main(String arg[]){
			
			/// 1. Crear e instalar un security manager.
			if(System.getSecurityManager()==null)
				System.setSecurityManager(new RMISecurityManager());
			try{
				System.out.println("Lado del Cliente -");
				/// 2. Bajarse la interfaz mediante RMI.
				HelloInt interf = (HelloInt)Naming.lookup("//hostRemoto/HelloServerBind");
				/// 3. Usar las funciones de la interfaz.
				System.out.println("Stub dice... " + interf.sayHello());
			}catch(Exception e){
				System.out.println("Exception: " +e.toString());
			}
	
		}
	}
	
Comentarios:
if(System.getSecurityManager()==null){
Igual que en el servidor.
System.setSecurityManager(new RMISecurityManager())
Igual que en el servidor, tener en cuenta que aqui nos bajaremos un "stub" y una interficie que pueden ser peligrosos para la seguridad del cliente, por eso es necesario también instalar un SecurityManager
HelloInt interf = (HelloInt)Naming.lookup(" ...
Para crear la interfaz se hace un cast de la clase que nos bajamos del servidor. Notar que el cliente necesita tener la interfaz HelloInt (fichero HelloInt.class para poder hacer el cast.
... Naming.lookup("//hostRemoto/HelloServerBind");
Hace la búsqueda mediante la función lookup al servidor que puede ser un nombre válido o una IP. Y el objeto que buscamos se debe de haber dado de alta con el nombre HelloServerBind
Se debe importar la clase java.rmi.Naming para dar de alta al objeto.

Compilación

Tenemos 3 ficheros por la parte del servidor (HelloInt.java, HelloImpl.java y HelloServer.java) que deben ser compilados a la vez. Y luego tenemos por parte del cliente el fichero HelloClientRMI.java.

Todos residirán en el directorio ejemplo:

J:\tutoriales\RMI> (Bajo windows)
$HOME/tutoriales/RMI (Bajo Linux-Unix)

Para compilar los ficheros del servidor introducir:

javac -d j:\tutoriales\RMI HelloImpl.java HelloInt.java HelloServer.java (Bajo windows)
javac -d $HOME/tutoriales/RMI HelloImpl.java HelloInt.java HelloServer.java (Bajo Linux-Unix)

La opción -d nos indica el directorio de los ficheros fuente. Al estar incluido en el package rmihello el compilador creará un directorio llamado rmihello y pondrá allí los ficheros .class

Luego tenemos que generar los Stubs y lo Skeletons con el compilador rmi de la siguiente manera:

rmic -d j:\tutoriales\RMI rmihello.HelloImpl (Bajo windows)
rmic -d $HOME/tutoriales/RMI rmihello.HelloImpl (Bajo Linux-Unix)

El Stub es la clase que trabaja en el cliente y el skeleton en el servidor y ambos encapsulan RMI. Los dos ficheros generados automáticamente son: HelloImpl_Stub.class y HelloImpl_Skel.class

Para compilar el cliente:

javac HelloClientRMI.java

Ejecución

Necesitamos ejecutar tres cosas: el RMIregistry, el servidor y el cliente.

1. Al ejecutar el registro RMI (RMIregistry) podrá dar de alta aplicaciones RMI y preguntar por ellas. Como se ha dicho antes este se da de alta en el puerto 1099 pero también se puede configurar. Si se quiere saber más sobre el rmiregistry visitar las páginas de java.sun.com según sea para Solaris o bien sea para Win32.

Para arrancar introducir en la línea de comandos:

start rmiregistry (Bajo windows)
rmiregistry & (Bajo Linux-Unix)

2. Al ejecutar el servidor damos de alta en el RMIregistry nuestra aplicación para que puedan acceder a ella desde otras partes. Y se ejecuta en la línea de comandos con:

java HelloServer

Esta operación es la más delicada y puede dar miles de fallos insospechados. Se sabe que todo ha funcionado correctamente si se observa un HelloServerBind dado de alta en el rmiregistry lo que indica que no ha habido ninguna Excepción.

Puede ser por el contrario que hayan errores, si la Java Virtual Machine nos 'vomita' un angustioso
     java.lang.ClassNotFoundException posiblemente sea porque los stubs no estan en el CLASSPATH.
También puede lanzarnos un
    java.security.AccessControlException: access denied
     (java.net.SocketPermission 127.0.0.1:1099 connect,resolve)

que además de pillarnos desprevenidos nos dolerá. Si aparece esto recomiendo rezar un par de padre nuestros, confesarse y pasarse por los links de la Bibliografía que nos indican cómo solucionarlo. Recuerdo que los Firewalls pueden bloquear el acceso a estos puertos.

3. Al arrancar el cliente desde cualquier máquina accedemos al servidor y este nos debe retornar el resultado de las operaciones que en este caso será un simple String.

En el lado del cliente deben estar presentes los ficheros HelloClientRMI.class (el cliente en sí), HelloInt.class (la interfaz necesaria para hacer el cast) y el HelloImpl_Stub.class (que es el Stub).

Ejecutar en la línea de comandos:

java HelloClientRMI

Espero que hayas aprendido algo y te hayas divertido también. Si hay cualquier comentario, fallos conceptuales, o lo que sea escribirme. Ahora bien si ejecutando esto destrozaís el ordenador, o entraís en el pentagono yo no tengo nada que ver, vaya me excluyo de toda responsabilidad.

Copyright © 2002 by Carlos Morales. All rights reserved.

Bibliografía

En la red:

http://java.sun.com/j2se/1.3/docs/guide/rmi/
Guía de aprendizaje de SUN.
http://java.sun.com/products/jdk/rmi/
Página principal de JavaTM Remote Method Invocation (RMI)
http://www.ccs.neu.edu/home/kenb/com3337/rmi_tut.html
Tutorial del rmi por un estudiante de la universidad de Northeastern University, Boston.

Libros de la compañía Sun:

Web:
	http://developer.java.sun.com/developer/infodocs/#books
Título:
	Advanced Programming for the JavaTM 2 Platform
Autores:
	Calvin Austin and Monica Pawlan
Publicado por:
	Addison Wesley Professional, ISBN: 0201715015, November 1999