Arquitectura Cliente/Servidor Request Cliente Invocación de Métodos Remotos RMI: Remote Method Invocation ¿ 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 ? Prof. Wílmer Pereira Universidad Simón Bolívar Llamadas a Métodos Remotos El El cliente cliente necesita necesita hacer hacer transparente transparente la la llamada llamada aa métodos métodos remotos 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. Servidor Reply La La invocación invocación del del servicio servicio remoto remoto es es aa partir partir de de un un objeto objeto (creado (creado en en el el cliente) cliente) que que llama llama aa un un método método (implementado (implementado en en el el servidor) servidor) La La idea idea es es tener tener un un contexto contexto de de ejecución ejecución parecido parecido al al local local bajo bajo una una arquitectura arquitectura cliente/servidor cliente/servidor 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 = ...; 1 Carga dinámica de clases Ejemplo: Interfaz e implantación Interfaz compartida por el cliente y el servidor: El El cliente cliente requiere requiere cargar cargar las las clases clases yy excepciones excepciones que que el el servidor servidor cambie, cambie, según según su su evolución evolución interface Product extends Remote { public String getDescription() throws RemoteException: } Carga dinámica con el ClassLoader Llamada del cliente: Product p; String d=p.getDescription(); Sin embargo ... esto implica problemas de seguridad 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; } En consecuencia ... se necesita al security manager para impedir llegada de virus a través de los stubs Jerarquía de clases Convenciones RMI Sufijos Object Sin: Remote RemoteObject Interfaz común cliente/servidor (Product) Impl: Clase del servidor que implemente la interfaz (ProductImpl ) Server: Clase que crea los objetos servidores (ProductServer ) RemoteStub RemoteServer Client: Clase que llama a métodos remotos (ProductClient) _Stub Subclase generada automáticamente por rmic (Product_Stub) UnicastRemoteObject SUN propone la clase MulticastRemote para manejar objetos replicados en múltiples servidores rmic genera el stub que es usado por el cliente rmic rmic ProductImpl ProductImpl 2 Servicio de registro de stubs El El servidor servidor carga carga los los stubs stubs por por cada cada uno uno de de sus sus objetos objetos (generado (generado por por rmic) rmic) para para que que pueda pueda ser ser descargado descargado por por cada cada objeto objeto del del cliente 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 (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 ( 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 (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 3 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 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 Políticas de seguridad de la aplicación El Elmanejador manejadorde deseguridad seguridadrestringe restringeaacualquier cualquier Código Códigode deser sercargado cargadodesde desdeelelcliente 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 java ProductClient ProductClient -Djava.security.policy=client.policy -Djava.security.policy=client.policy 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”; }; 4 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 javaProductServer ProductServer–Djava.rmi.server.codebase=http://localhost/download/ –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 javaProductClient ProductClient–Djava.security.policy=client.policy –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); 5