Copyright (c) 2008 Héctor Francisco Hernández <hectorfh@gmail.com>.
Se otorga permiso para copiar, distribuir y/o modificar este documento bajo los términos de la Licencia de Documentación Libre de GNU, Versión 1.3 o cualquier otra versión posterior publicada por la Free Software Foundation; sin Secciones Invariantes ni Textos de Cubierta Delantera ni Textos de Cubierta Trasera. Una copia de la licencia se encuentra en http://www.gnu.org/copyleft/fdl.html.
Introducción.
RMI es un protocolo propietario de Java que permite que métodos de objetos que existen en una máquina virtual puedan ser invocados desde otros objetos en otra máquina virtual, probablemente en un host distinto.
RMI sólo permite la comunicación entre tecnología Java. Si se requiere comunicar con otras tecnologías debe usarse CORBA o SOAP.
Al estar específicamente diseñado para Java, RMI puede darse el lujo de ser muy amigable para los programadores, proveyendo pasaje por referencia de objetos (cosa que no hace SOAP), "recolección de basura" distribuida y pasaje de tipos arbitrarios (funcionalidad no provista por CORBA).
Por medio de RMI un programa puede hacer disponible un objeto en la red. Otro programa entonces podría invocar métodos de dicho objeto como si invocara métodos de objetos propios, de un modo transparente al programador, sin tener que lidiar con sockets de bajo nivel.
Cuando un programador invoca un método de un objeto remoto, los parámetros son serializados (convertidos en un flujo de bytes capaz de ser enviados a través de un socket). Luego se envían a través de la red, mientras que el método que hizo la invocación queda esperando. El método remoto realiza el procesamiento, serializa el valor de retorno y se lo devuelve al llamador. Esto implica que el pasaje de parámetros que no tienen interfaz remota definida (objetos no remotos), cuando se trata de métodos remotos, es por copia.
Por este motivo se requiere que los parámetros del método remoto y del dato que retorna sean de clases que se puedan serializar (en Java, que implementen la interfaz "Serializable").
El encargado de hacer públicos los objetos remotos es el registro RMI. Se trata de un servicio, cuyo puerto TCP por defecto es el 1099 (aunque es posible elegir otro), que mantiene información acerca de los objetos exportados y sus correspondientes direcciones. El servidor debe registrar en él sus objetos remotos mediante el método "Naming.bind("dirección", objeto)" y el cliente debe solicitarlos llamando a "Naming.lookup("dirección")".
Haciendo uso de RMI.
Para aprender más hacemos uso de la tecnología por nuestra cuenta.Para utilizar objetos remotos, lo primero que debemos hacer es definir una interfaz con los métodos que serán utilizados remotamente. Está especificado que está interfaz debe heredar de la interfaz "Remote".
Los métodos remotos lanzan excepciones de la clase "RemoteException", y no de la clase "Exception".
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface InterfazRemota extends Remote
{
public String recibirMensaje(String mensaje) throws RemoteException;
}
Aunque el cliente no conozca la implementación del objeto remoto, deberá conocer su interfaz para invocar los métodos, por lo tanto deberemos compilar el cliente también utilizando esa interfaz.
Luego implementamos la clase del objeto remoto. Las clases remotas deben heredar de "UnicastRemoteObject".
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.net.InetAddress;
public class ClaseRemota extends UnicastRemoteObject
implements InterfazRemota
{
public ClaseRemota() throws RemoteException
{
}
public String recibirMensaje(String mensaje)
throws RemoteException
{
try {
System.out.println(mensaje);
return "Soy " +
InetAddress.getLocalHost().getHostAddress() +
" y el mensaje ha sido impreso en mi consola.";
}
catch (Exception e)
{
throw new RemoteException("Fallo en el servidor");
}
}
}
Ahora ya podemos terminar el servidor. Deberá instanciar el objeto y hacerlo disponible en la red a través del registro "RMI". Para eso existe la función "Naming.bind(dirección, objeto)" que asocia nuestro objeto remoto con un nombre virtual del formato "//host:port/name". Utilizaremos ese nombre para buscar el objeto remoto desde el proceso cliente más tarde.
import java.rmi.Naming;
import java.net.InetAddress;
public class Servidor
{
public static void main(String[] args)
{
try
{
InterfazRemota objetoRemoto = new ClaseRemota();
Naming.bind("//" +
InetAddress.getLocalHost().getHostAddress() +
":1234/PruebaRMI", objetoRemoto);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Hemos escogido el puerto "1234", sin embargo, de omitir el puerto TCP, RMI utiliza por defecto 1099.
El servidor ya está en condiciones de ser compilado y ejecutado. Para ello alcanzará con el comando "javac *java". Luego para correrlo debemos previamente ejecutar el registro RMI para el correspondiente puerto (comando "rmiregistry 1234" en nuestro glorioso Slackware GNU/Linux) y luego el programa servidor propiamente dicho (comando "java Servidor").
Ahora haremos un programita cliente que simplemente busque nuestro objeto remoto en la red y ejecute el método "recibirMensaje" con el parámetro "Hola Mundo!". Para ahorrar tiempo vamos a suponer que la dirección IP del servidor es "10.0.0.10", siendo esta la dirección de la máquina en la que se hizo el experimento.
Para encontrar "objetoRemoto" se utiliza la función "Naming.lookup(dirección)".
import java.rmi.Naming;
public class Cliente
{
public static void main(String[] args)
{
try
{
InterfazRemota objetoRemoto = (InterfazRemota)
Naming.lookup("rmi://10.0.0.10:1234/PruebaRMI");
String retval = objetoRemoto.recibirMensaje("Hola Mundo!");
System.out.println("Mensaje del servidor: " + retval);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Compilamos el cliente con el comando "javac Cliente.java" asegurándonos que "InterfazRemota" está en el "classpath" y lo ejecutamos. Como resultado veremos en la consola del servidor el mensaje "Hola Mundo!" que ha llegado en la invocación al método remoto, y en la del cliente "Mensaje del servidor: Soy 10.0.0.10 y el mensaje ha sido impreso en mi consola.".
Conclusión.
Según la experiencia que hemos tenido con RMI, todo se ve transparente y amigable al programador. Debido a que está pensado exclusivamente para Java, la integración con esta tecnología es perfecta y sin dolores de cabeza. Sin embargo nos vemos bastante limitados, para no decir del todo, en cuanto a interoperabilidad con el resto del universo no Java.Referencias.
- Sitio Oficial de Java (http://java.sun.com/)
- Wikipedia (http://es.wikipedia.org/)
- Sitio de O'Reilly (http://www.oreillynet.com/)
- Proactiva Calidad (http://www.proactiva-calidad.com/)
0 comentarios:
Publicar un comentario en la entrada