1. Objetivos:
2. Tareas a realizar:
Para la realización de este laboratorio se proporciona tres clases: EntradaSistemaBDRemoto.java, InterfaceRemotoLogicaNegocio.java y PresentacionRemoto.java, con el objeto de ejecutar los mismos usando una arquitectura física en 3 niveles, para ello:
1. Descomprimir el fichero Laboratorio4RMI.zip, el cual contiene las clases anteriores, la BD de passwords, un fichero con la política de seguridad y un fichero .bat que permite generar los Stubs y los Skeleton.
2. Crear un workspace y un proyecto donde añadáis las clases anteriores.
3. Comprobar qué clases corresponden al Servidor RMI, Cliente RMI e Interfaz Remota y completarlas si es necesario. Compilar las clases.
4. Lanzar en forma local el Servidor RMI, Cliente RMI y RMIRegistry en el orden apropiado. ¿Que sucede? Nos podemos comunicar.
5. Crear los Stubs y Skeletons (utilizar el fichero de comando crearStubs.bat, modificando lo que sea necesario) ¿Que sucede? Tenemos los permisos apropiados.
6. Definir la opción de seguridad al lanzar el cliente RMI y el servidor RMI
7. Ejecutar la aplicación en 3 niveles físicos: colocando cada nivel en una máquina diferente.
8. Ejecutar la aplicación en 3 niveles físicos, y además, realizando lo necesario para que en la máquina cliente, la que ejecuta el nivel de presentación, no esté la clase STUB.
Os recomiendo que intentéis realizar lo anterior consultando en primer lugar el ejemplo orientativo y las transparencias de la asignatura pensando un poco lo que se está pidiendo. Pero, si queréis una ayuda más detallada de lo que se pide, consultad este otro documento o bien el siguiente documento que contiene un resumen de RMI.
3. Ejemplo orientativo: Aquí se proporciona un ejemplo muy simple de RMI que suma dos numeros, basado en el tutorial http://www.chuidiang.com/java/rmi/rmi.html. Este ejemplo nos servira de guía pero antes de mostrar el ejemplo recordaremos qué necesitamos para implementar RMI.
InterfaceRemota. La cual contendra los métodos que deseamos invocar de forma remota, es decir, los métodos que queremos llamar desde el cliente, pero que se ejecutarán en el servidor.
ObjetoRemoto. Es la clase que implementa los métodos de la InterfaceRemota. Esta clase sólo la conoce el servidor de RMI.
ObjetoRemoto_Stubs. Esta clase es la que conoce el cliente y no necesitamos codificarla, java lo hace automáticamente a partir del ObjetoRemoto. En otras palabras, es una clase que implementa InterfaceRemota, pero cada método se encarga de hacer una llamada a través de red al ObjetoRemoto del servidor, esperar el resultado y devolverlo
FicheroDeSeguridad. En este fichero indica qué conexiones pueden o no establecerse entre el servidor y el cliente RMI. Debe haber un fichero de política de seguridad (java.policy) tanto en el Servidor como en el Cliente RMI.
En el servidor se debe ejecutar dos programas
RMIREGISTRY. Proporcionado por Java con el objetivo de registrar los objetos que pueden ser invocados remotamente, además de gestionar las peticiones de los clientes.
Servidor. Basicamente es una clase a construir que debe instanciar el ObjetoRemoto y registrarlo en el rmiregistry.
En el el cliente se debe correr un programa
Cliente. Consiste en una clase que debe pedir al servidor una referencia al ObjetoRemoto (es decir, ObjetoRemoto_Stbus) para así poder invocar a los métodos. Los métodos se ejecutarán en el Servidor, pero Cliente quedará bloqueado hasta que Servidor termine de ejecutar el método.
Veamos ahora los elementos antes enunciado pero en mayor profundidad.
InterfaceRemota
Lo primero que se debe hacer es una interface con los métodos a invocar. Esta
interface tiene que tener el siguiente aspecto:
import java.rmi.Remote;
public interface InterfaceRemota extends Remote {
public int suma (int a, int b) throws
java.rmi.RemoteException;
}
Para que el objeto sea remoto debe heredar de la interface Remote. Además de
incorporar los métodos que queramos invocar acompañados cada uno de ellos con
una excepxion (java.rmi.RemoteException) la cual se producirá si hay algún
problema con la comunicación entre los dos ordenadores o cualquier otro problema
con RMI.
Otro aspecto importantes es que todos los parámetros y valores devueltos por
estos métodos deben ser tipos primitivos de java o bien clases que implementen
la interface Serializable de java. De esta forma, tanto los parámetros como el
resultado podrán "viajar" por red del cliente al servidor y al revés.
ObjetoRemoto
Al ser la clase que implementa la InterfaceRemota debe a su vez implementar la
interface Remote de java. Obtener una referencia remota a objetos trae consigo
una serie de complejidades que afortunademante son manejadas por java mediante
la herencia de la clase UnicastRemoteObject. Obviamente no es necesario
heredar de UnicastRemoteObject, pero la forma de registrar e implemntar los
métodos varía un poco.
import java.io.Serializable;
public class ObjetoRemoto extends UnicastRemoteObject
implements InterfaceRemota {
public int suma(int a, int b) {
System.out.println ("sumando "+ a +" + "+ b +"...");
return a+b;
}
}
ObjetoRemoteStubs
Tras compilar el objeto remoto en el servidor es necesario crear la "clase de stubs". Esta
clase Strubs es una clase que contiene los mismos métodos que nuestro
ObjetoRemoto, pero con capacidad de gestionar el envio y recepción de mensaje
por red. Java proporciona una herramienta llamada RMIC la cual recibe como
parametro la clase ObjetoRemoto y nos devuelve la clase de stubs
ObjetoRemoto_stubs. Para ello es necesario configurar el directorio en el
cual se encuentra nuestra clase ObjetoRemoto y ejecutar rmic
$ set
CLASSPATH=C:\Lab4
$ rmic ObjetoRemoto
Esto generarará un ObjetoRemoto_stubs.class y un ObjetoRemoto_Skel.class. El
primero debe estar visible tanto en el cliente como en el servidor, es decir,
deben aparecer en el CLASSPATH de ambos. Eso implica que debe estar situado en
el servidor en un sitio público al que el cliente tenga acceso o que se debe
suministar una copia al cliente. El ObjetoRemoto_Skel.class se generá por
defecto y sólo es útil para clientes con java anterior a la versión 1.2. Para
los java más modernos no tiene utilidad.
Nota: En JDeveloper RMIC se encuentra en "C:\Archivos de programa\Oracle\JDev9i\jdk1.3\bin\rmic"
FicheroDeSeguridad
El fichero de permisos por defecto debe llamarse java.policy y estar en el HOME
del usuario que lanza el servidor o el cliente de RMI. Se debe agregar la
siguente linea al fichero:
grant { permission
java.security.AllPermission; };
Esta no es la opción más segura, pero de momento nos vale. Si al ejecutar la
aplicación tenemos problemas de permiso debemos forzar el uso de este fichero;
esto se hace agregando la propiedad "java.security.policy" de la siguiente
manera o bien por línea de comando.
System.setProperty ("java.security.policy","HOME/java.policy");
ServidorRMI
Sera el encargado de instanciar y registrar el objeto remoto. Para ello se debe indicar, en formato URL, el path donde se encuentra el objeto remoto.
System.setProperty ("java.rmi.server.codebase",
"file://localhost/prueba_servidor/");
Instanciar una clase remota y luego registrarla en el servidor de RMI es
sencillo.
ObjetoRemoto
objetoRemoto = new ObjetoRemoto();
Naming.rebind ("ObjetoRemoto", objetoRemoto);
Para registrarla hay que llamar al método estático rebind() de la
clase Naming. Se le pasan dos parámetros. Un nombre para poder identificar el
objeto y una instancia del objeto. El nombre que hemos dado debe conocerlo el
cliente, para luego poder pedir la instancia por el nombre. El método rebind()
registra el objeto. Si ya estuviera registrado, lo sustituye por el que acabamos
de pasarle.
ClienteRMI
Es quien utiliza el objeto de forma remota. Los pasos que debe realizar este
programa son los siguientes. Pedir el objeto remoto al servidor de rmi. El
código para ello es sencillo
InterfaceRemota objetoRemoto = (InterfaceRemota)
Naming.lookup ("//localhost/ObjetoRemoto");
Simplemente se llama al método estático lookup() de la clase Naming. Se le pasa
a este método la URL del objeto. Esa URL es el nombre (o IP) del host donde está
el servidor de rmi y por último el nombre con el que se registró anteriormente
el objeto.
Este método devuelve un Remote, así que debemos hacer un "cast" a
InterfaceRemota para poder utilizarlo. El objeto que recibimos aquí es realmente
un ObjetoRemoto_Stubs. Ahora estamos en condiciones de invocar al método
suma
System.out.print ("2 + 3 = ");
System.out.println (objetoRemoto.suma(2, 3));
Para que el código del cliente compile necesita ver en su classpath a
InterfaceRemota.class. Para que además se ejecute sin problemas necesita además
ver a ObjetoRemoto_Stubs.class, por lo que estas clases deben estar accesibles
desde el servidor o bien tener copias locales de ellas.