Invocación de Métodos Remotos RMI: Remote Method Invocation Prof. Wílmer Pereira Universidad Simón Bolívar Arquitectura Cliente/Servidor Request Cliente Servidor Reply ¿ Cómo permitir comunicación entre objetos situados en diferentes máquinas ? ¿ Cómo llamar a métodos de programas que corren en máquinas diferentes ? La Lainvocación invocacióndel delservicio servicioremoto remotoes esaapartir partirde deun unobjeto objeto (creado (creadoen enelelcliente) cliente)que quellama llamaaaun unmétodo método (implementado (implementadoen enelelservidor) servidor) La Laidea ideaes estener tenerun uncontexto contextode deejecución ejecuciónparecido parecidoal allocal local bajo bajouna unaarquitectura arquitecturacliente/servidor cliente/servidor Llamadas a Métodos Remotos El Elcliente clientenecesita necesitahacer hacertransparente transparentelalallamada llamada aamétodos métodosremotos remotos=> =>stubs stubs El stub paquetiza los parámetros (marshalling) y los codifica en un formato estándar independientes de los procesadores involucrados en la aplicación cliente/servidor Big-endian El stub da la impresión de localidad al cliente, asegurando transparencia El servidor asegura: • • • • desempaqueta los parámetros (unmarshalling), llama al método invocado, paquetiza los valores de retorno o excepción (marshalling) envía información al cliente. Principios de una llamada remota Llamada al método remoto desde el cliente: centralWarehouse.getQuantity(“SuperSucket 100”); Interfaz común al cliente y servidor: interface Warehouse { public int getQuantity(String description) throws RemoteException: . . . } Declaración del objeto local: Warehouse centralWarehouse = ...; Carga dinámica de clases El Elcliente clienterequiere requierecargar cargar las lasclases clasesyyexcepciones excepciones que queelelservidor servidorcambie, cambie, según segúnsu suevolución evolución Carga dinámica con el ClassLoader Sin embargo ... esto implica problemas de seguridad En consecuencia ... se necesita al security manager para impedir llegada de virus a través de los stubs Ejemplo: Interfaz e implantación Interfaz compartida por el cliente y el servidor: interface Product extends Remote { public String getDescription() throws RemoteException: } Llamada del cliente: Product p; String d=p.getDescription(); Implantación del método remoto: public class ProductImpl extends UnicastRemoteException implements Product { public ProductImpl(String d) throws RemoteException { descr=d; } public String getDescription() throws RemoteException { return “Esto es un”+descr; } private String descr; } Jerarquía de clases Object Remote RemoteObject RemoteStub RemoteServer UnicastRemoteObject SUN propone la clase MulticastRemote para manejar objetos replicados en múltiples servidores Convenciones RMI Sufijos Sin: Interfaz común cliente/servidor (Product) Impl: Clase del servidor que implemente la interfaz (ProductImpl) Server: Clase que crea los objetos servidores (ProductServer) Client: Clase que llama a métodos remotos (ProductClient) _Stub Subclase generada automáticamente por rmic (Product_Stub) rmic genera el stub que es usado por el cliente rmic ProductImpl Servicio de registro de stubs El Elservidor servidorcarga cargalos losstubs stubspor porcada cadauno unode desus susobjetos objetos (generado (generadopor porrmic) rmic)para paraque quepueda puedaser ser descargado descargadopor porcada cadaobjeto objetodel delcliente cliente Carga el stub en servidor de registro (bind) //servidor ProductImpl pl = new ProductImpl(); Naming.bind(“toaster”,pl); Descarga el stub del servidor de registro (lookup) //cliente Product p = (Product) Naming.lookup (“rmic://www.ldc.usb.ve/toaster”); El puerto por defecto es el 1099 La aplicación servidor debe correr en la misma máquina del servidor de registro (por razones de seguridad) Código de ejemplo (Servidor) import java.rmi.*; import java.rmi.server*; public class ProductServer { public static void main(String args[]) { try { ProductImpl p1 = new ProductImpl(“Black Toaster”); ProductImpl p2 = new ProductImpl(“Express Oven”); Naming.rebind(“toaster”,p1); Naming.rebind(“oven”,p2); } catch (Exception e) { System.out.println(“Error: “+e); } } } Antes de correr el servidor debe levantarse el servidor de registro El servidor queda levantado indefinidamente con un thread que lanza el objeto de UnicastRemoteObject Código de ejemplo (Implantación del Servidor) import java.rmi.*; import java.rmi.server*; public class ProductImpl extends UnicastRemoteObject implements Product { public ProductImpl(String n) throws RemoteException { name = n; } public String getDescription() throws RemoteException { return “Est es un “+name; } private String name; } A partir de esta clase se crean los stubs que deben cargarse en el servidor de registro (bind) y ser bajados por el cliente (lookup) Los métodos que llama el cliente están implantados en esta clase Código de ejemplo (Interfaz) import java.rmi.*; public interface Product extends Remote { String getDescription() throws RemoteException; } La interfaz debe estar tanto en el servidor como en el cliente de la aplicación Inicialmente se instala el servidor de registro rmiregistry rmiregistry && Se ejecuta el servidor de la aplicación para cargar los stubs en el servidor de registro que fue instalado antes java java ProductServer ProductServer Codigo de ejemplo (Cliente) import java.rmi.*; import java.rmi.server*; public class ProductClient { public static void main(String args[]) { System.setSecurityManager(new RMISecurityMananger()); String url = “rmi://localhost/” try { Product c1 = (Product)Naming.lookup(url+”toaster”); Product c2 = (Product)Naming.lookup(url+”oven”); System.out.println(c1.getdescription()); System.out.println(c2.getdescription()); } catch (Exception e) { System.out.println(“Error: “+e); } System.exit(0); } } El programa instala el manejador de seguridad y se baja los stubs (uno por cada objeto del cliente) para poder llamar al método remoto Políticas de seguridad de la aplicación El manejador de seguridad restringe a cualquier Código de ser cargado desde el cliente Sin embargo el cliente necesita conexión para: Bajar los stubs del servidor de registro Acceder a los objetos remotos para llamar a los métodos remotos En consecuencia se debe suplir un archivo de seguridad: cliente.policy grant { permission java.net.SocketPermission “*:1024-65535”,”connect”; }; java ProductClient -Djava.security.policy=client.policy Correr la aplicación (localmente) Compilar fuentes en el servidor javac Product*.java Correr rmic para generar los stubs rmic ProductImpl Instalar el servidor de registro rmiregistry & Instalar el servidor java ProductServer & Correr el cliente java –Djava.security.policy=cliente.policy ProductClient Directorios y archivos Servidor Cliente ProductServer.class ProductClient.class ProductImpl.class Product.class Product.class client.policy ProductImpl_Stub.class Download ProductImpl_Stub.class Product.class Correr la aplicación (remotamente) Para bajar los stubs debe haber un servidor Web corriendo en el servidor Se puede instalar un servidor Web sencillo desde ftp://java.sun.com/pub/jdk1.1/rmi/class-server.zip Mover el directorio download al directorio del servidor Web Cambiar el client.policy para conectarse al servidor de registro, implantaciones del servidor y el puerto del HTTP. Todo esto indicando la máquina remota grant { permission java.net.SocketPermission “servok.ldc.usb.ve:1024-65535”,”connect”; permission java.net.SocketPermission “servok.ldc.usb.ve:80”,”connect”; }; Aplicación con servidor Web Levantar el rmiregistry en un shell sin classpath y desde un directorio sin .class Desde un shell levantar el servidor dando el URL del directorio download java ProductServer –Djava.rmi.server.codebase=http://localhost/download/ Modificar el URL en el cliente para poder acceder al servidor remoto String url = “rmi://servok.ldc.usb.ve/” Product cl= (Product)Naming.lookup(url+”tostador”); Correr el cliente indicando las nuevas políticas de seguridad java ProductClient –Djava.security.policy=client.policy Observaciones del enfoque RMI Este enfoque permite ejecutar concurrente el mismo método por varios clientes. Sin embargo si se requiere exclusión mutua, sobre el método, se puede utilizar la primitiva syncronize. No se tiene acceso a los mismos descriptores de I/O entre el cliente y el servidor. Los stubs se encargan de los parámetros y lo único no permitido en los métodos remotos, es el pasaje de parámetros por referencia. La única condición es que los objetos pasados como parámetros deben implementar la interfaz Serializable. No se pueden sobrescribir los métodos equals, clone y paint. Deben escribirse métodos propios para realizar dichas tareas. Cuando se implementan la aplicación sobre applets, no se puede modificar las restricciones de seguridad que impone el browser. Es conveniente usar levantar el servidor de registro (rmiregistry) desde el servidor para evitar su proliferación en varias máquinas. El comando es: LocateRegistry.createRegistry(port);