Está en la página 1de 12

Seminario

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

Seminario 4. Programación básica en RMI



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)

4.2 Ejecución de aplicaciones RMI



Para comprender cómo se ejecutan las aplicaciones RMI comencemos por
identificar los componentes a ejecutar:
• El servicio de nombres (rmiregistry)
• La aplicación (servidor) que contiene el objeto remoto (en el ejemplo de
este documento “CifradorRMI”)
• La aplicación (cliente) que utiliza el objeto remoto (en el ejemplo de este
documento “Cifrante”)

Pág: 4
ETSINF-UPV SDI

Si intentamos ejecutar estos componentes directamente como aplicaciones Java, se


producirán errores. Es necesario establecer correctamente las políticas de
seguridad y los lugares de confianza de RMI para la descarga de clases.

Comencemos por ejecutar todos los componentes desde eclipse en la misma
máquina.

4.2.1 Ejecución de cliente y servidor en el mismo host



Para probar el código que se suministra como ejemplo, cree dos proyectos en
Eclipse tal y como indica la siguiente figura:




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.

Leyendo el código del servidor y del cliente observará que se instancia y


establece en el sistema un objeto “SecurtyManager” esta objeto establece en la
máquina virtual Java los permisos de ejecución que lee de un archivo que se
especifica en la propiedad de la JVM “java.security.policy”. Esto implica que
debemos escribir el archivo de política de seguridad y establecer la propiedad
“java.security.policy” de la JVM para que apunte al archivo que hemos escrito.

Pág: 5
ETSINF-UPV SDI

Para las pruebas, estableceremos una política de seguridad altamente permisiva


creando un archivo con el nombre “java.policicy” con el siguiente contenido:

grant {
permission java.security.AllPermission "", "";
permission java.net.SocketPermission "*", "connect,accept,listen,resolve";
};

Para la ejecución tanto del cliente como del servidor, estableceremos esta política
de seguridad escribiendo la propiedad de la JVM en la línea de órdenes con la
opción “–D” así:

> java -Djava.security.policy=java.policy ...

Otro aspecto importante en la ejecución de aplicaciones RMI es la especificación
del lugar de confianza de descarga de clases. Esta ubicación se establece mediante
la propiedad “java.rmi.server.codebase”.

En particular, el servicio de nombre “rmiregistry” necesita conocer la estructura de
los interfaces de los objetos que se vayan a registrar en él. Para que el “rmiregistry”
pueda descargar estos interfaces hay que especificar en la línea de ejecución la
propiedad “java.rmi.server.codebase”.

Abramos una consola y ejecutemos el rmiregistry así:



> rmiregistry –J-D java.rmi.server.codebase=file:///c:/sdi/ws/00_basico/rmicifint/bin/

Es importante que no olvide la barra final (…/bin/) en la especificación de la


ruta.

Ahora en Eclipse ejecutemos el servidor. Para ello, creemos un perfil de


ejecución escribamos en el cuadro de texto “VM arguments” la especificación de la
ruta del fichero de política de seguridad:

-Djava.security.policy=java.policy

Ahora ejecutemos el cliente, estableciendo de forma idéntica a como hemos


hecho en el servidor el fichero de política de seguridad en “VM arguments”:

-Djava.security.policy=java.policy

y en el cuadro de texto “Program arguments” el host donde se encuentra en
ejecución el “rmiregistry”, en nuestro caso actual “localhost”. A modo de guía,
puede observar la configuración del perfil de ejecución del cliente en la siguiente
figura:

Pág: 6
ETSINF-UPV SDI

4.2.1 Ejecución del servidor en un host remoto



Para probar la ejecución distribuida de una aplicación RMI haremos un montaje
como el de la siguiente figura.



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.

Observe que el código del servidor establece la propiedad


“java.rmi.server.hostname” mediante el código:

System.setProperty("java.rmi.server.hostname",localIP);

Esto es necesario porque si no se hace así, el servidor se enlazaría a la dirección IP
127.0.0.0 y, por lo tanto, operaría exclusivamente de forma local.

Pág: 8
ETSINF-UPV SDI

4.3 Ejemplo: Objeto remoto para cifrar y descifrar cadenas de caracteres



En este ejemplo implementaremos un servicio para cifrar/descifrar cadenas de
caracteres mediante una simple operación XOR.

4.3.1 Interfaz. Fichero CifradorInt.java



package rmi;

public interface CifradorInt extends java.rmi.Remote {

public String cifra(String input, String clave) throws java.rmi.RemoteException;


public String descifra(String input, String clave) throws java.rmi.RemoteException;

4.3.2 Funcionalidad del servicio. Fichero Cifrador.java



package server;
import java.rmi.RemoteException;

import rmi.CifradorInt;

public class Cifrador implements 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;

public class CifradorRMI implements CifradorInt{

private Cifrador cif = new Cifrador();

protected CifradorRMI() {
super();
}

@Override
public String cifra(String input, String clave) throws RemoteException {

Pág: 9
ETSINF-UPV SDI

return cif.cifra(input, clave);


}

@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");

4.3.4 Objeto cliente. Fichero Cifrante.java



package client;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import rmi.CifradorInt;

public class Cifrante {

public static void main(String[] args) {

if (args.length<1){
System.out.println("Uso echo <host>");System.exit(1);
}

String host = args[0];

if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}

CifradorInt cif = null;


try {
System.out.println("Locating registry at "+host+ " port 1099");
Registry reg = LocateRegistry.getRegistry(host,1099);
System.out.println("Lookingup micifradorXOR ");
cif = (CifradorInt) reg.lookup("micifradorXOR");
//cif = (CifradorInt) Naming.lookup("micifradorXOR");
String men = "ABCDEFGHI";
String menCifrado = "";
System.out.println("Calling remote cipher ");
menCifrado = cif.cifra(men, "pepone");

System.out.println("Mensaje cifrado: "+ menCifrado);


System.out.println("Mensaje descifrado: "+ cif.descifra(menCifrado, "pepone"));

} catch (RemoteException | NotBoundException e1) {

Pág: 10
ETSINF-UPV SDI

e1.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}

4.3.1 Utilidad de red. Fichero Net.java



package util;

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;

public class Net {

public static String chooseOperatingIPAddr() {

ArrayList<String> retval = new ArrayList<String>();


Enumeration<NetworkInterface> n;
try {
n = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e1) {
return "localhost";
}
for (; n.hasMoreElements();)
{
NetworkInterface e = n.nextElement();
Enumeration<InetAddress> a = e.getInetAddresses();
for (; a.hasMoreElements();)
{
InetAddress addr = a.nextElement();
if (addr instanceof Inet6Address) {
continue;
} else {
if (addr.isLoopbackAddress()) {
continue;
} else {
retval.add(addr.getHostAddress());
}
}
}
}
if (retval.size() > 1) {
System.out.println("Choose a operating IP address");
int i = 0;
for (String addr : retval) {
System.out.println(" (" + i + ") " + addr);
i++;
}
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
int option = -1;
while ((option < 0) || (option > retval.size()-1)) {
try {
option = Integer.parseInt(stdin.readLine());
} catch (Exception e) {
option = -1;
}
}
return retval.get(option);
//System.out.println(retval.get(option));
} else {
if (retval.size() == 1) {
return retval.get(0);
//System.out.println(retval.get(0));
} else {
return "localhost";
//System.out.println("localhost");
}

Pág: 11
ETSINF-UPV SDI

}
}

Pág: 12

También podría gustarte