Documentos de Académico
Documentos de Profesional
Documentos de Cultura
4
Programación básica en RMI
Diseño y Aplicaciones de Sistemas
Distribuidos
(SDI)
José Simó
Rev: Septiembre 2016.
ETSINF-UPV SDI
Contenido
Seminario 4. Programación básica en RMI ........................................................... 3
4.1 Introducción .............................................................................................................................................. 3
4.2 Ejecución de aplicaciones RMI .......................................................................................................... 4
4.2.1 Ejecución de cliente y servidor en el mismo host ...................................................................... 5
4.2.1 Ejecución del servidor en un host remoto .................................................................................... 7
4.3 Ejemplo: Objeto remoto para cifrar y descifrar cadenas de caracteres .......................... 9
4.3.1 Interfaz. Fichero CifradorInt.java .................................................................................................... 9
4.3.2 Funcionalidad del servicio. Fichero Cifrador.java .................................................................... 9
4.3.3 Objeto remoto. Fichero CifradorRMI.java .................................................................................... 9
4.3.4 Objeto cliente. Fichero Cifrante.java ........................................................................................... 10
4.3.1 Utilidad de red. Fichero Net.java .................................................................................................. 11
Pág: 2
ETSINF-UPV SDI
4.1 Introducción
JAVA-RMI (Java Remote Method Invocation) es un sistema de invocación de
métodos remotos que (a diferencia de CORBA) asume un entorno homogéneo de
Máquina Virtual Java. Proporciona un modelo de objetos distribuidos similar al de
Java, integrándolo en el lenguaje de manera natural. En este sentido permite:
• Invocar métodos de interfaces remotas
• Pasar una referencia a un objeto remoto en una invocación (local o remota)
• Soportar callbacks de servidores a Applets.
Presenta algunas limitaciones respecto al modelo de objetos de Java
• Los clientes siempre interaccionan con las interfaces, nunca con las
implementaciones.
• Los argumentos no remotos de la invocaciones y los resultados siempre se
pasan por copia, nunca por referencia.
• Los objetos remotos se pasan por referencia no por copia
• Existen excepciones adicionales que pueden ocurrir durante una RMI.
Los componentes de una aplicación RMI son:
• Interfaz: el objeto que queramos ofrecer para su uso remoto tiene que
estar especificado en términos de su interfaz. Es conveniente observar que
en Java el mismo objeto declarado como perteneciente a paquetes
diferentes es esencialmente un objeto diferente. Por esta razón es
importante declarar el interfaz en un paquete separado que puedan usar
tanto el cliente como el servidor. Así aseguramos que tanto e cliente como
el servidor usan el mismo interfaz para el objeto remoto.
• Objeto servidor: es una implementación del interfaz declarado
anteriormente. Para convertir este objeto en un objeto accesible por RMI es
necesario que derive de la clase UnicastRemoteObject o exportarlo
mediante el método de clase UnicastRemoteObject.exportObject(…).
Pág: 3
ETSINF-UPV SDI
• Stubs y Skeletons: son las clases impostoras que permiten el manejo del
objeto remoto de forma transparente como si fuera local. Se generan
automáticamente por un preprocesador llamado rmic. El rmic produce los
stubs y skeletons a partir del fichero de implementación del objeto servidor
(no a partir de su interfaz). A partir de Java 1.4, se generan en tiempo de
ejecución de forma transparente y no es necesario ejecutar de forma
explícita el rmic.
• El servicio de nombres rmiregistry: en este servicio de nombres se
registra el objeto servidor para que los clientes puedan obtener referencia a
él interrogando al servicio. En todo host donde residan objetos servidores,
debe ejecutarse un rmiregistry. Este servidor sólo mantiene entradas de
servicios locales. El rmiregistry soporta un espacio de nombres plano. Los
nombres pueden incluir cualquier carácter y son sensibles a mayúsculas.
• Objeto cliente: es un objeto que usa la funcionalidad ofrecida remotamente
por el objeto servidor. El objeto cliente obtiene una referencia al objeto
remoto interrogando al rmiregistry. Para su funcionamiento, el objeto
cliente sólo necesita conocer el interfaz del servicio.
Los pasos para generar una aplicación RMI
• Escribir el interfaz del objeto remoto
• Escribir una implementación del interfaz definido junto con el código de
registro RMI
• Escribir el acceso al objeto remoto en una aplicación cliente
• Definir la política de seguridad (java.policy)
Pág: 4
ETSINF-UPV SDI
Siguiendo nuestra forma habitual de trabajo, supondremos que estos proyectos se
ubican en las rutas siguientes:
C:\sdi\ws\00_basico\rmicif
C:\sdi\ws\00_basico\rmicifint
Observe que un proyecto, el “rmicifint” contiene únicamente el interfaz del objeto
remoto. Esto lo hacemos para aislar las dependencias (la localización de confianza
para la descarga de clases RMI) como veremos más adelante.
El otro proyecto, el “rmicif” contiene sendos paquetes para cliente y servidor en el
que se encuentran las clases correspondientes. Además contiene un paquete con
una utilidad de red necesaria.
Pág: 5
ETSINF-UPV SDI
Pág: 6
ETSINF-UPV SDI
Usaremos un sistema empotrado “BeagleBone Black” que ejecuta un S.O. Linux
Debian completo. Supondremos en este ejemplo que la dirección IP de este
computador es “192.168.1.45”
Como requisito inicial, tendremos que instalar el jdk (en este ejemplo el jdk 1.8)
mediante las siguientes órdenes:
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
//y las variables de entorno:
$ sudo apt-get install oracle-java8-set-default
También instalaremos un servidor web “tomcat” en el directorio
/root/sdi/servers/tomcat8
Pág: 7
ETSINF-UPV SDI
Utilizaremos ese servidor web como lugar de confianza para la descarga de clases
RMI, es decir, que la propiedad de la JVM “java.rmi.server.codebase” citada
anteriormente apuntará a la url donde ubiquemos estas clases.
En el host BBB, si el “tomcat” está instalado en “/root/sdi/servers/tomcat8”, cree
los siguientes directorios:
“/root/sdi/servers/tomcat8/webapps/ROOT/rmi”
y dentro de él:
“/root/sdi/servers/tomcat8/webapps/ROOT/rmi/codebase”
Copie los archivos “.class” de los paquetes “servidor” y “rmi” de los proyectos
Eclipse “rmicif” y “rmicifint” respectivamente, de modo que existan los directorios:
“/root/sdi/servers/tomcat8/webapps/ROOT/rmi/codebase/server”
y
“/root/sdi/servers/tomcat8/webapps/ROOT/rmi/codebase/rmi”
Copie el archivo de política de seguridad descrito en la sección anterior a la
ubicación:
“/root/sdi/servers/tomcat8/webapps/ROOT/rmi/codebase/java.policy”
Cambie el puerto de defecto del tomcat del 8080 al 8081 (para evitar conflictos)
editando el archivo “/root/sdi/servers/tomcat8/conf/server.xml”
Arranque el tomcat:
> cd /root/sdi/servers/tomcat8/bin
> ./startup.sh
Ejecute el rmiregistry en la BBB:
> rmiregistry -J-Djava.rmi.server.codebase=http://192.168.1.45/rmi/codebase/&
Ejecute el programa servidor en la BBB
> java -Djava.security.policy=./java.policy server.CifradorRMI
Vuelva al host inicial (normalmente la máquina virtual Windows) y ejecute el
cliente poniendo ahora como parámetro del programa la IP del computador BBB
(192.168.1.45).
Compruebe que todo funciona correctamente.
Pág: 8
ETSINF-UPV SDI
import rmi.CifradorInt;
@Override
public String cifra(String input, String clave) throws RemoteException {
String respuesta = new String("");
if (clave.length()==0) return input;
int j=0;
for(int i=0;i<input.length();i++) {
respuesta += (char)((int)input.charAt(i) ^ (int)clave.charAt(j));
j = (j+1)%clave.length();
}
return respuesta;
}
@Override
public String descifra(String input, String clave) throws RemoteException {
return cifra(input,clave);
}
}
4.3.3 Objeto remoto. Fichero CifradorRMI.java
package server;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedHashMap;
import rmi.CifradorInt;
import util.Net;
protected CifradorRMI() {
super();
}
@Override
public String cifra(String input, String clave) throws RemoteException {
Pág: 9
ETSINF-UPV SDI
@Override
public String descifra(String input, String clave) throws RemoteException {
return cif.descifra(input, clave);
}
/**
* @param args
*/
public static void main(String[] args) {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
//System.setProperty("java.rmi.server.hostname","192.168.1.45");
String localIP = Net.chooseOperatingIPAddr();
System.out.println("The local IP address is " + localIP);
System.setProperty("java.rmi.server.hostname",localIP);
try {
Registry registry = LocateRegistry.getRegistry();
CifradorInt stub = (CifradorInt) UnicastRemoteObject.exportObject(new
CifradorRMI(),0);
registry.rebind("micifradorXOR", stub);
} catch (RemoteException e) {
System.err.println("Error en el sistema remoto:" + e.getMessage());
System.exit(-1); // can't just return, rmi threads may not exit
}
System.out.println("Servidor de cifrado preparado");
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import rmi.CifradorInt;
if (args.length<1){
System.out.println("Uso echo <host>");System.exit(1);
}
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
Pág: 10
ETSINF-UPV SDI
e1.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
Pág: 11
ETSINF-UPV SDI
}
}
Pág: 12