Conceptos de Programació Programación Distribuida TEMA 7: Paso de Mensajes con RMI CONTENIDO: Conceptos de Programación Distribuida Remote Method Invocation (RMI) en Java El Nivel de Resguardos La Responsable de que Todo Funcione: la interfaz El precompilador rmic Arquitectura Completa de una Aplicación Evoluciones Reseñables ¾ ¾ ¾ ¾ BIBLIOGRAFÍA: ¾ [Eck02] Eckel, B. Piensa en Java. Prentice Hall, 2002. [Hil00] Hilderink et al. Communicating Threads for Java. Draft. [Vin97] Vinoski, S. CORBA: Integrating Diverse…IEEE Communications Magazine, vol. 35, nº 2, February, 1997. [Gro01] Grosso, W. Java RMI. O’Reilly, 2001. ¾ No existe memoria común Comunicaciones No existe un estado global del sistema Grado de Transparencia Escalables Reconfigurables 1 2 Modelo de Paso de Mensajes Modelos de Programació Programación Distribuida ¾ ¾ Modelo de Paso de Mensajes ; Operaciones ¾ ¾ send y receive ¾ Modelo RPC ¾ Bloqueadas-No bloqueadas Almacenadas-No almacenadas (en buffer) ¾ Fiables-No Fiables ¾ ; Stubs de cliente y servidor ; Visión en Java es RMI ¾ Mecanismo para comunicar y sincronizar entidades concurrentes que no pueden o no quieren compartir memoria Llamadas send y receive Tipología de las llamadas: ¾ En Unix: ¾ En Java: ¾ Modelos de Objetos Distribuidos ; DCOM de Microsoft de Sun ; CORBA de OMG ¾ ; Jini ¾ Pipes con y sin nombre Sockets: Clases Socket y ServerSocket Canales: CTJ (Communicating Threads for Java) 3 Modelo General 4 Puertos (Breve repaso) ¾ SOLICITUD CLIENTE El protocolo TCP (y también UDP) utilizan los puertos para hacer llegar los datos de entrada a un proceso concreto que se ejecuta en una máquina SERVIDOR RESPUESTA NUCLEO NUCLEO P U E R T O CLIENTE RED SERVIDOR TCP/UDP 5 6 1 Modelo Remote Procedure Call (RPC) P U E R T O CLIENTE ¾ ¾ SERVIDOR ¾ ¾ Solicitud de conexión ¾ Mayor nivel de abstracción Llamadas a procedimiento local o remoto indistintamente Necesita de stubs/skeletons (¡OCULTAMIENTO!) Registro del servicio (Servidor de Nombres) En Unix: ¾ Biblioteca rpc.h Representación estándar XDR ¾ Automatización parcial: especificación-compilador rpcgen ¾ CLIENTE P U E R T O P U E R T O ¾ En Java: ¾ Objetos remotos. Serialización Paquete java.rmi ¾ Automatización parcial: interfaz-compilador rmic SERVIDOR ¾ PUERTO 7 8 Llamando a un procedimiento remoto Espacio del usuario Introducción del nivel de stubs Espacio del usuario Programa cliente Programa servidor func() func(void) {//codigo …………. } OBJETIVOS: 1. Efectuar la llamada como si tuviera carácter local 2. Enmascarar aspectos relativos a comunicaciones 3. ¿Y cómo lograrlo…? Host local ¾ ¾ ¾ ¾ ¾ ¾ Proceso Cliente 10 ¿Y cómo generar los stubs? Proceso Servidor ¾ Programa Cliente Funciones Servidor llamada normal rpcreturn Stub del Cliente Servicios de Red Núcleo Cliente TCP UDP Host remoto 9 Esquema RPC con stubs callrpc Efectúan el marshalling/unmarshalling de parámetros. Bloquean al cliente a la espera del resultado. Transparencia de ejecución remota Interface con el nivel de red. De forma manual De forma automática ¾ Especificación return Stub del Servidor RED ¾ formal de interfaz específico ¾ rpcgen (C-unix)/rmic (Java) ¾ Software Servicios de Red Núcleo Servidor 11 12 2 Dinamic Binding Generación automática de stubs Especificación Formal Interface Cliente Ejecutable C O M P I L A R Servidor Ejecutable Precompilador rpcgen o rmic Resguardo Stub Cliente Ficheros compartidos Representación de datos Resguardo Skeleton Servidor C O M P I L A R Generador de Resguardos Especificación Formal Interface Interfaz del servidor+asa create, read delete, write Res. Del Servidor Read(f,d) CONECTOR SERVIDOR Especificación de la interfaz del servidor remoto en un fichero de especificación .x /*rand.x*/ program RAND_PROG{ version RAND_VER{ void inicia_aleatorio(long)=1; double obtener_aleat(void)=2; }=1; }=0x3111111; ¾ ¾ CLIENTE Envío de asa 14 RMI (Remote Method Invocation) en Java Breve Nota sobre RPC en C ¾ d Petición de asa del servidor 13 ¾ Res. Del Cliente Generación autómática de resguardos: $rpcgen rand.x Distribuir y compilar ficheros entre las máquinas implicadas Necesario utilizar representación XDR. Biblioteca xdr.h ¾ ¾ ¾ Permite disponer de objetos distribuidos utilizando Java Un objeto distribuido se ejecuta en una JVM diferente o remota Objetivo: lograr una referencia al objeto remoto que permita utilizarlo como si el objeto se estuviera ejecutando sobre la JVM local ¾ Es similar a RPC, si bien el nivel de abstracción es más alto ¾ Se puede generalizar a otros lenguajes utilizando JNI ¾ RMI pasa los parámetros y valores de retorno utilizando serialización de objetos. Objeto Cliente Objeto Servidor JVM JVM 15 RPC versus RMI RPC 16 Llamadas locales vs. Llamadas remotas RMI ¾ Un objeto remoto es aquél que se ejecuta en una JVM diferente, situada potencialmente en un host distinto. RMI es la acción de invocar a un método de la interfaz de un objeto remoto. Carácter y Estructura de diseño procedimental Carácter y Estructura de diseño orientada a objetos Es dependiente del lenguaje Es dependiente del lenguaje Utiliza la representación externa de datos XDR Utiliza la serialización de objetos en Java El uso de punteros requiere el manejo explícito de los mismos El uso de referencias a objetos locales y remotos es automático dato = Suma (x,y); NO hay movilidad de código El código es móvil, mediante el uso de bytecodes. //ejemplo de llamada a método remoto (no completo) ¾ //ejemplo de llamada a método local int dato; IEjemploRMI1 ORemoto = (IEjemploRMI1)Naming.lookup(“//sargo:2005/EjemploRMI1”); ORemoto.Suma(x,y); 17 18 3 La clase java.rmi.Naming Arquitectura RMI Cliente Servidor Stub Skeleton Referencia Remota Referencia Remota Transporte Transporte ¾ El servidor debe extender RemoteObject ¾ El servidor debe implementar una interfaz diseñada previamente ¾ El servidor debe tener como mínimo un constructor (nulo) que lanzará la excepción RemoteException ¾ El método main del servidor debe lanzar un gestor de seguridad ¾ El método main crea los objetos remotos ¾ El compilador de RMI (rmic) genera el stub y el skeleton. ¾ Los clientes de objetos remotos se comunican con interfaces remotas (diseñadas antes de…) ¾ Los objetos remotos son pasados por referencia ¾ Los clientes que llaman a métodos remotos deben manejar excepciones. public static void bind(String name, Remote obj) public static String[] list(String name) public static Remote lookup(String name) public static void rebind(String name, Remote obj) public static void unbind(String name) 19 20 ¿Cómo lograrlo? implements Definir Interfaz Remota Implementar Interfaz Remota public interfaz extends Remote {signaturas métodos} implements $ rmic .class resultado Implementación de la interfaz ref.metodo(param) Cliente Skeleton Stub new JVM SERVIDOR referencia a interfaz lookup Nombre Referencia servicio ref JVM bind ó rebind Implementar Servidor 21 22 FASES DE DISEÑO RMI (1-INTERFACE) ¾ ¾ ¾ ¾ ¾ ¾ ¾ Escribir el fichero de la interfaz remota. Debe ser public y extender a Remote. Declara todos los métodos que el servidor remoto ofrece, pero NO los implementa. Se indica nombre, parámetros y tipo de retorno. Todos los métodos de la interfaz remota lanzan obligatoriamente la excepción RemoteException El propósito de la interface es ocultar la implementación de los aspectos relativos a los métodos remotos. De esta forma, cuando el cliente logra una referencia a un objeto remoto, en realidad obtiene una referencia a una interfaz. Los clientes envían sus mensaje a los métodos de la interfaz Implementar Cliente EJEMPLO (FICHERO DE INTERFAZ) /**Ejemplo del interfaz remoto para implementar un RMI * @author Antonio Tomeu *Es un servidor remoto aritmético. */ //se importan las clases del paquete rmi import java.rmi.*; //toda interface remota debe extender la clase Remote public interface IEjemploRMI1 extends Remote { //todo metodo de la interfaz remota debe lanzar la //excepcion RemoteException int Suma(int x, int y) throws RemoteException; int Resta(int x, int y) throws RemoteException; int Producto(int x, int y) throws RemoteException; float Cociente(int x, int y) throws RemoteException; } 23 24 4 FASES DE DISEÑO RMI (2-IMPLEMENTACIÓN) ¾ ¾ ¾ ¾ ¾ ¾ ¾ La implementación del servidor es un fichero que realiza la implementación de la interfaz definida previamente. El servidor debe contener una clase que extienda a UnicasRemoteObject. Esa misma clase debe implementar a la interfaz remota (implements) Debe tener un constructor que lance RemoteException. El método main debe lanzar un gestor de seguridad. El método main debe crear los objetos remotos deseados. El método main debe registrar al menos unos de los objetos remotos. EJEMPLO (IMPLEMENTACIÓN DE INTERFAZ Y SERVIDOR) /**Ejemplo de implementacion del interfaz remoto para * @author Antonio Tomeu */ un RMI //se importan los paquetes necesarios import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.net.*; //el servidor debe siempre extender a UnicastRemoteObject //el servidor debe simpre implementar la interfaz remota public class EjemploRMI1 extends UnicastRemoteObject implements IEjemploRMI1 { 25 //aquí viene la implementación de los métodos que se //declararon en la interfaz public int Suma(int x, int y) throws RemoteException {return x+y;} 26 //es necesario que haya un constructor (nulo) como minimo, ya //que debe lanzar RemoteException public EjemploRMI1() throws RemoteException {//super();} //el metodo main siguiente realiza el registro del servicio public int Resta(int x, int y) throws RemoteException {return x-y;} public int Producto(int x, int y) throws RemoteException {return x*y;} public float Cociente(int x, int y) throws RemoteException { if(y == 0) return 1; else return x/y; } //a continuacion viene el codigo del servidor. Hemos optado //por incluirlo de manera conjunta, pero podia haber ido en //un fichero aparte. 27 FASES DE DISEÑO RMI (3- Generando STUB Y SKELETON) Fichero de implementación de Interfaz compilado (Imp.class) Fichero de stub (Imp_Stub.class) Compilador de RMI $ rmic imp Fichero de skeleton (Imp_Skel.class) $ rmic –vcompat EjemploRMI1 ¾ Es necesario que en la máquina remota donde se aloje el servidor se sitúen también los ficheros de stub y skeleton. (Cambios a partir de 1.5) ¾ Con todo ello disponible, se lanza el servidor llamando a JVM del host remoto, previo registro en un DNS ¾ En nuestro caso, el servidor remoto se activó en hercules.uca.es sobre el puerto 1099. $ java EjemploRMI1 & 29 public static void main(String[] args) throws Exception { //crea e instala un gestor de seguridad que soporte RMI. //el usado es distribuido con JDK. Otros son posibles. System.setSecurityManager( new RMISecurityManager()); //Se crea el objeto remoto. Podriamos crear mas si interesa. EjemploRMI1 ORemoto = new EjemploRMI1(); //Se registra el objeto en la máquina remota. No hay que dar //nombre de host, y se asume el puerto 1099. Naming.bind(“Servidor",ORemoto); System.out.println("Servidor Remoto Preparado"); } } 28 FASES DE DISEÑO RMI (4-REGISTRO) ¾Método Naming.bind(“Servidor",ORemoto); para registrar el objeto remoto creado requiere que el servidor de nombres esté activo. ¾ Dicho servidor se activa con start rmiregistry en Win32 ¾ Dicho servidor se activa con rmiregistry & en Unix. ¾ Se puede indicar como parámetro el puerto que escucha. ¾ Si no se indica, por defecto es el puerto 1099. ¾ El parámetro puede ser el nombre de un host como en Naming.bind(“//sargo.uca.es:2005/Servidor",ORemoto); 30 5 FASES DE DISEÑO RMI (5-CLIENTE) ¾ ¾ ¾ ¾ El objeto cliente procede siempre creando un objeto de interfaz remota. Posteriormente efectúa una llamada al método Naming.lookup cuyo parámetro es el nombre y puerto del host remoto junto con el nombre del servidor. Naming.lookup devuelve una referencia que se convierte a una referencia a la interfaz remota. A partir de aquí, a través de esa referencia el programador puede invocar todos los métodos de esa interfaz remota como si fueran referencias a objetos en la JVM local. 31 EJERCICIO Distribución de Archivos Cliente EJEMPLO (CLIENTE) /**Ejemplo de implementacion de un cliente para RMI * @author Antonio Tomeu */ import java.rmi.*; import java.rmi.registry.*; public class ClienteEjemploRMI1 { public static void main(String[] args) throws Exception { int a = 10; int b = -10; //Se obtiene una referencia a la interfaz del objeto remoto //SIEMPRE debe convertirse el retorno del metodo Naming.lookup //a un objeto de interfaz remoto IEjemploRMI1 RefObRemoto = (IEjemploRMI1)Naming.lookup("//hercules.uca.es/Servidor"); //Llamamos a los metodos del interfaz remoto. System.out.println(RefObRemoto.Suma(a,b)); System.out.println(RefObRemoto.Resta(a,b)); System.out.println(RefObRemoto.Producto(a,b)); System.out.println(RefObRemoto.Cociente(a,b)); } 32 } ¾ Descargue los ficheros IEjemploRMI1.java, EjemploRMI1.java y ClienteEjemploRMI1.java ¾ Siga el guión auxiliar de prácticas (ver bloque del tema actual) y desarrolle la arquitectura en local Distribuya ahora la aplicación con un compañero Escriba una interfaz de operaciones para números complejos, utilizando tipos primitivos, llamada IComplejo.java Impleméntela en Complejo.java Servidor Interfaz.class Interfaz.class ¾ Servidor.class Cliente.class ¾ Implem_Stub.class Implem_Stub.class Implem_Skel.class NOTA: A PARTIR DE jdk 1.2 LAS CLASES DE SKELETON NO SON NECESARIAS 33 ¾ ¾ ¾ Acuerden una interfaz llamada interfaz_grupo_x.java Un miembro del grupo escribirá el cliente_grupo_java y el otro el servidor_grupo_x.java de manera independiente. Una vez hecho, lancen la arquitectura rmi. Suban el Espacio de los alumnos un fichero rmi_grupo_x.rar que contegan los tres ficheros. 35 ¾ ¾ Escriba código de servidor y cliente que hagan uso de la misma. Guárdelos en ServerComplejo.java y ClientComplejo.java ¾ Distribuya nuevamente la aplicación 34 EVOLUCIONES RESEÑABLES A partir del jdk 1.2 la implementación de RMI no necesita skeletons (con ese nombre tenían poco futuro). El stub estará en ambos lados. ¾ Recompile los ejemplos anteriores sin el flag –vcompat ¾ A partir del jdk 1.5 se incorpora Generación Dinámica de Resguardos ¾ 36 6 EJERCICIOS RMI: CARACTERÍSTICAS AVANZADAS Pruebe los ejemplos anteriores sin skeleton ¾ No obstante, ¿necesita el servidor algo a pesar de todo ? ¾ ¾ ¾ Serialización de Objetos Gestión de la Seguridad: policytool Callback de Cliente ¾ Descarga Dinámica de Clases ¾ 37 38 SERIALIZACIÓN: La interfaz Serializable ¾ Serializar un objeto es convertirlo en una cadena de bits, posteriormente restaurada en el objeto original ¾ Es útil para enviar objetos complejos en RMI ¾ Tipos primitivos se serializan autómaticamente ¾ Clases contenedoras también ¾ Objetos complejos deben implementar la interfaz Serializable (es un flag) para poder ser serializados Ejemplo con serialización de una clase import java.io.*; import java.util.*; public class cuentabanca implements Serializable { int ccc; float saldo; String Titular; public cuentabanca(int codigo, float deposito, String nombre) {ccc = codigo; saldo = deposito; Titular = nombre;} public String toString() {return("codigo = "+ccc+" saldo= "+saldo+" titular= "+Titular);} } 39 40 Interfaz Ejemplo completo con serialización (1/4) import java.io.*; import java.rmi.*; public interface intefaz extends Remote { public class Arbol implements java.io.Serializable //convierte a la clase en serializable { public Arbol izq; public Arbol der; public int id; public int nivel; private static int cont = 0; boolean deposito(int codigo, float cantidad) throws RemoteException; boolean reintegro(int codigo, float cantidad) RemoteException; public Arbol(int l) { id = cont++; nivel = l; if (l > 0) { izq = new Arbol(l-1); der = new Arbol(l-1); } } throws String nomtit(int codigo) throws RemoteException; int codtit(String nom) throws RemoteException; /aquí la serializacion es necesaria*/ cuentabanca [] listado() throws RemoteException; } } 41 42 7 Ejemplo completo con serialización (2/4) Ejemplo completo con serialización (3/4) import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; //interfaz que transfiere al servidor una instancia de la clase serializable Arbol.java import java.rmi.*; public interface IArbol extends Remote { public void Listado_Remoto (Arbol t, int n) throws RemoteException; } 43 Ejemplo completo con serialización (4/4) public class Serv_arbol extends UnicastRemoteObject implements IArbol { public Serv_arbol()throws RemoteException {super();} public void Listado_Remoto (Arbol t, int n) throws RemoteException { for (int i = 0; i < t.nivel; i++) System.out.print(" "); System.out.println("nodo " + t.id); if (t.nivel <= n && t.izq != null) Listado_Remoto(t.izq, n); if (t.nivel <= n && t.der != null) Listado_Remoto(t.der, n); } public static void main(String[] args) throws Exception { Serv_arbol ORemoto = new Serv_arbol(); Naming.bind("Servidor", ORemoto); System.out.println("Servidor Remoto Preparado"); } } 44 Ejecución del ejemplo en modo local import java.rmi.*; import java.rmi.registry.*; public class Client_arbol { public static void main(String[] args) throws Exception { int niveles = 3; Arbol arb = new Arbol (niveles); //se crea un objeto serializable IArbol RefObRemoto = (IArbol)Naming.lookup("//localhost/Servidor"); RefObRemoto.Listado_Remoto (arb, niveles); //transfiere al objeto //servidor un objeto Arbol //serializado } } 45 EJERCICIOS 46 Gestionando la seguridad: policytool ¾ Descargue los ficheros contenidos en la subcarpeta Serializacion (carpeta de códigos del tema), compile y pruebe Haga lo propio distribuyendo la aplicación con un compañero Java tiene en cuenta la seguridad System.setSecurityManager(new RMISecurityManager()); ¾ Se ajusta la seguridad: ¾ ¾ ¾ Clase RMISecurityManager ¾ Programador ajusta la seguridad mediante la clase Policy ¾ Policy.getPolicy() permite conocer la seguridad actual. Policy.setPolicy() permite fijar nueva política. ¾ Seguridad reside en fichero específico. ¾ ¾ 47 Construyendo un objeto SecurityManager Llamando al método setSecurityManager (clase System) Java2 incorpora una herramienta visual: policytool 48 8 ¾ Ajuste de la política de seguridad ¾ Crearlas con policytool: ejemplo de fichero .policy /* AUTOMATICALLY GENERATED ON Fri May 28 19:23:13 CEST 2004*/ /* DO NOT EDIT */ grant { permission java.net.SocketPermission "*:1024-65535", "connect,accept, listen, accept, connect, listen, resolve"; permission java.net.SocketPermission "*:80", "connect, accept"; permission java.security.AllPermission; }; ¾ Activarlas ¾ Ejecutar ajustando la política con Java –Djava.security=fichero.policy servidor|cliente ¾ O bien crear objeto RMISecurityManager y ajustar SetSecurityManager sobre el objeto creado. 49 RMI con Polling: Ejemplo de interfaz CallBack de Cliente Si servidor de RMI debe notificar eventos a clientes, la arquitectura es inapropiada ¾ La alternativa estándar es el sondeo (polling) ¾ Donde cliente: ¾ Iservidor RefRemota= (IServidor) Naming.lookup (URL); while (!(Ref.Remota.Evento.Esperado())){} 50 RMI con Polling: Ejemplo de servidor import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; import java.rmi.*; public interface ejemploPolling extends Remote { public void datoInc() throws RemoteException; public boolean igualDiez() throws RemoteException; //metodo para realizar el sondeo continuo sobre el servidor public class servPolling extends UnicastRemoteObject implements ejemploPolling { private static int dato = 0; public void datoInc() throws RemoteException {dato++; System.out.println(dato);} public boolean igualDiez() throws RemoteException //metodo para recibir el sondeo {return (dato==10);} } public servPolling() throws RemoteException {} public static void main(String[] args) throws Exception { servPolling contRemoto = new servPolling(); Naming.bind("Servidor_Polling", contRemoto); System.out.println("Servidor Remoto Preparado"); } } 51 RMI con Polling: Ejemplo de cliente 52 Características del CallBack import java.rmi.*; import java.rmi.registry.*; public class clientPolling { Clientes interesados se registran en un objeto servidor para que les sea notificado un evento ¾ Cuando el evento se produce, el objeto servidor notifica al cliente su ocurrencia ¾ ¿Qué necesitamos para que funcione? ¾ public static void main(String[] args) throws Exception { ejemploPolling RefObRemoto = (ejemploPolling)Naming.lookup("//localhost/Servidor_Polling"); while(!RefObRemoto.igualDiez()) //lazo de sondeo continuo al sevidor… {System.out.println("Incremento remoto del contador"); RefObRemoto.datoInc(); } System.out.print("El contador remoto llego a diez y se sale..."); } } 53 54 9 Conclusiones Arquitectura RMI para CallBack Hay que duplicar la arquitectura Se requieren dos interfaces Habrá dos niveles de stubs Cliente (Stub+Skel) y Servidor (Stub+Skel) Es necesario proveer en el servidor medios para que los clientes registren sus peticiones de callback 55 EJERCICIOS 56 Descarga Dinámica de Clases Lea el guión de callback de cliente Compile y active el código ejemplo de callback que figura en la subcarpeta callback de la carpeta codigos rmi (utilice stubs y skeletons) ¾ Haga lo propio con subcarpeta callback_simpel ¾ Distribuya la aplicación con su compañero de grupo ¾ ¾ Permite cambios libres en el servidor ¾ Elimina la necesidad de distribuir (resguardos) si los hay y nuevas clases ¾ Dinámicamente, el cliente descarga todas las clases que necesita para desarrollar una RMI válida ¾ Mediante HTTP o incluso FTP ¾ 57 (2)Cliente solicita interfaz vía Naming.lookup Registro de RMI (1) Servidor registra el par (Objeto, nombre) 58 Servidor HTTP para Descarga Dinámica ¾ (3)Registro ofrece una referencia remota Mini-servidor de HTTP de Sun ¾ Clases ¾ Se Servidor RMI Cliente de RMI server.codebase= http://host/directorio (4)Cliente solicita la clase de stub mediante el codebase ¾ ¾ ClassServer y ClassFileServer compilan conjuntamente java ClassFileServer 8080 /rmi/clases Servidor HTTP estándar ¾ Apache Localización URL ¾ Otros http (5)Servidor http devuelve la clase de stub host 59 60 10 Novedades a partir de jdk 1.5 Soporta Generación Dinámica de Resguardos ¾ Prescinde del generador rmic ¾ ¾ ¾ Ejemplo del Servidor Básico de Sun activo en Windows ¾ Colocar Resguardos y clases para carga dinámica en /java/rmi ¾ Ajustar la propiedad java.rmi.server.codebase 61 Pruebe ahora los ejemplos anteriores sin pasar por la etapa de generación de resguardos (rmic) ¿qué observa? 62 11