+ All Categories
Home > Documents > 5.1 Introducción a las tecnologías de objetos distribuidos...

5.1 Introducción a las tecnologías de objetos distribuidos...

Date post: 03-Nov-2018
Category:
Upload: phamkhanh
View: 214 times
Download: 0 times
Share this document with a friend
36
5.1 Introducción a las tecnologías de objetos distribuidos con Java RMI
Transcript

5.1 Introducción a las tecnologías de objetos distribuidos con Java RMI

Contenidos

Tutorial de Java RMI

Caso de estudio: diseño e implementación de la capa modelo de MiniBank con Java RMI

Arquitectura en 3 capas

Introducción

Java Remote Method Invocation (RMI)Permite definir e implementar interfaces remotos en el lenguaje Java

Sus operaciones se pueden invocar remotamente, de la misma forma que se invocan las operaciones de interfaces locales

Solución de más alto nivel que el uso directo de sockets o RPCs

Facilidad de desarrollo

Apropiado para construir sistemas cliente/servidor en una intranet, especialmente si cliente y servidor están escritos en Java

RMI también puede funcionar sobre IIOP (CORBA)

Ejemplo Clock

ServidorCliente

TimeOfDay time = clock.getTimeOfDay()

Clock

Estructura de paquetes del tutorial de Java RMI

es.udc.fbellas.j2ee.rmitutorial.clock

client

rmiinterface

server

es.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface

public interface Clock extends Remote {

public TimeOfDay getTimeOfDay() throws RemoteException;

}

public class TimeOfDay implements Serializable {

private int hour;private int minute;private int second;

public TimeOfDay(int hour, int minute, int second) {this.hour = hour;this.minute = minute;this.second = second;

}

// Métodos getXXX/setXXX ...

}

Interfaces remotos

Un interfaz remoto ha de extender, directa o indirectamente, el interfaz java.rmi.RemoteOperaciones

Los tipos de los parámetros y del resultado han de ser serializablesHan de declarar la excepción java.rmi.RemoteException(además de las propias)

Paso de parámetrosLos objetos locales se pasan por valor (serializados)Los objetos remotos se pasan por referencia

es.udc.fbellas.j2ee.rmitutorial.clock.server.ClockImpl

class ClockImpl implements Clock {

public TimeOfDay getTimeOfDay() {

int hour = Calendar.getInstance().get(Calendar.HOUR);int minute = Calendar.getInstance().get(Calendar.MINUTE);int second = Calendar.getInstance().get(Calendar.SECOND);

return new TimeOfDay(hour, minute, second);

}

}

Stubs y skeletons (1)

<<interface>>Clock

ClockImpl_Stub

Resto aplicacióncliente

Subsistema RMI

<<interface>>java.rmi.server.Skeleton

ClockImpl_Skel ClockImpl

getTimeOfDay

dispatch

Cliente Servidor

Subsistema RMI

getTimeOfDay

Stubs y skeletons (2)

StubClase usada por el cliente en sustitución de la remotaImplementa el interfaz remoto (ej.: Clock)La implementación de cada operación envía un mensaje a la máquina virtual que ejecuta el objeto remoto y recibe el resultado

Los parámetros y el valor de retorno se envían serializados

En términos de patrones, un stub es un Proxy

SkeletonClase usada por el servidorRecibe los mensajes, invoca la operación del objeto que implementa el interfaz remoto y envía el resultado al llamadorEn términos de patrones, un skeleton es un Adapter

Los Stubs y skeletons son transparentes al código

Stubs y skeletons (y 3)Generación de stubs y skeletons

Hasta J2SE 1.4 (inclusive) era preciso usar el compilador de RMI (comando rmic) para generar las clases Stub y SkeletonLa implementación de RMI en J2SE 5.0 hace uso del sistema de introspección de Java (java.lang.reflect)

El sistema de introspección permite dinámicamente averiguar información sobre clases, crear nuevas clases, construir instancias e invocar sus métodosLa implementación de RMI genera dinámicamente (no se genera código) la clase que implementa el stub y obvia la necesidad de tener un skeleton específico => no es necesario usar el compilador de RMI

Recepción y paso de referencias a objetos remotosCuando un cliente invoca una operación remota que devuelve una referencia a un objeto remoto, obtiene una instancia del stub correspondienteCuando un cliente invoca una operación remota en la que pasa una referencia a un objeto remoto, el servidor obtiene una instancia del stub correspondiente

Servicio de nombres

ProblemaEl servidor crea un objeto que implementa el interfaz Clock, ¿ Cómo obtienen los clientes una referencia a ese objeto ?

Servicio de nombresPermite asociar nombres lógicos (ej.: clock) a objetosServidor: asocia un nombre a un objetoCliente: obtiene una referencia al objeto a partir del nombreObjetivo: independencia de ubicación

Si en el futuro el objeto lo implementa otro servidor, o el servidor se cambia de máquina, no es necesario recompilar los clientes

ServidorCliente Servicio denombres

sn.bind(“clock”, c)c = sn.lookup(“clock”)

El servicio de nombres de Java RMI

Java RMI define un servicio de nombres muy sencilloEl esquema de nombrado sigue la sintaxis de una URL

//máquina:puerto/nombreDeObjeto, siendo nombreDeObjeto un nombre simple (ej.: clock)máquina y puerto hacen referencia a la máquina en la que corre el servicio de nombres (y no el objeto remoto)Por defecto, máquina = localhost y puerto = 1099

Interfaz java.rmi.registry.RegistryPermite asociar nombres simples a objetos

java.rmi.registry.Registrypublic interface Registry extends Remote {

public static final int REGISTRY_PORT = 1099;

public java.rmi.Remote lookup (String name) throws java.rmi.RemoteException, java.rmi.NotBoundException,

java.rmi.AccessException;

public void bind (String name, Remote obj)throws java.rmi.RemoteException,

java.rmi.AlreadyBoundException, java.rmi.AccessException;

public void unbind (String name)throws java.rmi.RemoteException, java.rmi.NotBoundException,

java.rmi.AccessException;

public void rebind (String name, Remote obj)throws java.rmi.RemoteException, java.rmi.AccessException;

public String[] list ()throws java.rmi.RemoteException, java.rmi.AccessException;

}

rmiregistry

Aplicación que contiene un objeto que implementa el interfaz java.rmi.registry.RegistryNo es persistentePor motivos de seguridad, la implementación de rmiregistry prohíbe que se invoquen los métodos bind, rebind y unbind de su objeto Registry desde otra máquina

ServidorCliente

rmiregistry

r.bind(“clock”, c)c = r.lookup(“clock”) Reg

istr

y

Máquina A Máquina B

java.rmi.Naming (1)

Clase utilidad con métodos estáticos, análogos a los de java.rmi.registry.Registry, para registrar/contactar con objetos a partir de una URL

Ejemplo:Clock clock = (Clock) Naming.lookup(

“//knopfler/clock”);

La implementación de Naming.lookup contacta con un objeto que implementa java.rmi.registry.Registry(utilizando la clase utilidad java.rmi.registry.LocateRegistry), que está en la máquina knopfler, escuchando por el puerto 1099, e invoca la operación lookup(“clock”)

java.rmi.Naming (y 2)public final class Naming {

public static Remote lookup (String url)throws RemoteException, NotBoundException,

java.net.MalformedURLException { ... }

public static void bind (String url, Remote obj)throws RemoteException, AlreadyBoundException,

java.net.MalformedURLException { ... }

public static void unbind (String url)throws RemoteException, NotBoundException,

java.net.MalformedURLException { ... }

public static void rebind (String url, Remote obj)throws RemoteException, java.net.MalformedURLException { ... }

public static String[] list (String name)throws RemoteException, java.net.MalformedURLException { ... }

}

es.udc.fbellas.j2ee.rmitutorial.clock.server.Serverclass Server {

public static void main (String[] args) {

/** Create an instance of "ClockImpl", export it, obtain a stub* and register the stub in the RMI registry.*/

try {

ClockImpl clockImpl = new ClockImpl();Clock clockStub = (Clock)

UnicastRemoteObject.exportObject(clockImpl, 0);

Naming.rebind("clock", clockStub);System.out.println("Server is working ...");

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

}

}

}

Comentarios (1)

Tipos de objetos remotos en Java RMIUnicast remote objectsActivable objects

Unicast remote objectEl servidor necesita estar constantemente en funcionamientoPermanece siempre en memoria

Activable objectEl servidor se activa la primera vez que se invoca uno de sus métodosEl servidor puede decidir desactivarlo cuando ningún cliente lo está utilizando (es decir, sale de memoria, pero no se destruye) y activarlo más tarde (cuando algún cliente vuelve a utilizarlo)

Utilidad: implementación de servidores que gestionan una gran cantidad de objetos

Comentarios (y 2)

Lo objetos Clock del ejemplo son de tipo “Unicast Remote Object”

Se “exportan” mediantejava.rmi.server.UnicastRemoteObject.exportObject

Permite que subsistema RMI atienda a peticiones dirigidas hacia ellosDevuelve un stub del objeto ClockNOTA: Existen varias versiones de este método. El ejemplo utiliza una que recibe como argumentos: el objeto y un número de puerto. Para que funcione el sistema dinámico de stubs y skeletons de J2SE 5.0 hay que usar 0 como número de puerto

es.udc.fbellas.j2ee.rmitutorial.clock.client.Client (1)

class Client {

public static void main (String[] args) {

/* * Get RMI registry address, which may be specified as * "hostName[:port]".*/

String registryAddress;

if (args.length == 0) {registryAddress = "localhost";

} else {registryAddress = args[0];

}

es.udc.fbellas.j2ee.rmitutorial.clock.client.Client (y 2)

try {/* Get a reference to the object implementing "Clock". */String clockURL = "//" + registryAddress + "/clock";Clock clock = (Clock) Naming.lookup(clockURL);

/* Obtain the time of day and print it. */TimeOfDay timeOfDay = clock.getTimeOfDay();System.out.println("Time of day: " + timeOfDay);

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

}

}

}

Evaluación del servicio de nombres de Java RMI

No cumple la propiedad de independencia de ubicación

La URL del objeto lleva el nombre y puerto de la máquina en la que corre el rmiregistry

Si el rmiregistry cambia de máquina o puerto, es necesario recompilar los clientesAlternativamente, el nombre y puerto se pueden pasar como parámetros desde la línea de comandos (como en el ejemplo) o leerlos de un fichero de configuración

Aún así, es preciso cambiar la configuración en los clientes

No permite tener una estructura jerárquica de nombrado (ej.: /objects/rmitutorial/clock), con posible federación en servidores

No es escalable

Política Multi-thread

Invocaciones desde distintas máquinas virtuales sobre un mismo objeto

Se crea un thread por cada invocación en el servidor (“thread-per-request”)

Invocaciones desde una misma máquina virtual sobre un objeto remoto

No se garantiza que se cree un thread por cada invocación en el servidor

ConclusiónLa clase que implementa un interfaz remoto necesita ser thread-safe

Ejecución (1)

ClockServer.jares.udc.fbellas.j2ee.rmitutorial.clock.server.*es.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface.*

ClockClient.jares.udc.fbellas.j2ee.rmitutorial.clock.client.*es.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface.*

ClockRMIInterface.jares.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface.*

Ejecución – ClockServer.sh (2)

#!/bin/sh

# Inclusion of common environment variables.. $J2EE_EXAMPLES_HOME/Scripts/CommonEnvironmentVariables.sh

# Class path.CP=$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/ClockServer.jar

# Code base.CODE_BASE="file:$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\ClockRMIInterface.jar"

# Start server.$JAVA_VIRTUAL_MACHINE -classpath $CP \

-Djava.rmi.server.codebase="$CODE_BASE" \es.udc.fbellas.j2ee.rmitutorial.clock.server.Server

Ejecución – ClockClient.sh (3)

#!/bin/sh

# Inclusion of common environment variables.. $J2EE_EXAMPLES_HOME/Scripts/CommonEnvironmentVariables.sh

# ---------------------------------------------------------------------# Optionally can receive the RMI registry address (with the format# host[:port]).# ---------------------------------------------------------------------

# Class path.CP=$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/ClockClient.jar

# Start client.$JAVA_VIRTUAL_MACHINE -classpath $CP \

es.udc.fbellas.j2ee.rmitutorial.clock.client.Client "$@"

Ejecución – codebase (4)

El script del servidor invoca a la máquina virtual con la “propiedad del sistema” java.rmi.server.codebase

Permite especificar un conjunto de clases con notación similar a la de un classpath (usando blancos como separador en vez de los caracteres “:”o “;”)Cuando una aplicación recibe un stub de otra (en este caso el rmiregistry recibe un stub del objeto Clock en el punto 1), si no tiene disponible el interfaz que implementa el stub en su classpath, lo carga del classpath especificado en el codebase de la aplicación que se lo envía (en este caso, ClockServer)

ClockServer.sh

ClockClient.sh

rmiregistry

1: Naming.bind(“clock”, c)3: c = Naming.lookup(“clock”)

2: Carga el interfaz Clock del codebase del servidor

4: t = c.getTimeOfDay()

Máquina A Máquina B

Ejecución – codebase (y 5)

NOTALas “propiedades del sistema” son argumentos que se le pasan a la máquina virtual antes del nombre de la clase que se desea ejecutar, usando la notación

-Dnombre=valor

Las propiedades del sistema se pueden recuperar con el método estático

java.lang.System.getProperties()

es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi (1)

AccountFacade

+ createAccount(accountVO : AccountVO) : AccountVO+ findAccount(accountIdentifier : Long) : AccountVO+ addToAccount(accountIdentifier : Long, amount : double) : void+ withdrawFromAccount(accountIdentifier : Long, amont : double) : void+ findAccountsByUserIdentifier(userIdentifier : Long, startIndex : int, count : int) : AccountChunkVO+ removeAccount(accountIdentifier : Long) : void+ transfer(sourceAccountIdentifier : Long, destinationAccountIdentifier : Long, amount : double) : void+ findAccountOperationsByDate(accountIdentifier : Long, startDate : Calendar, endDate : Calendar, startIndex : int, count : int) : AccountOperationChunkVO

<<Interface>>

Remote(from rmi)

<<Int erface>>

AccountFacadeImpl- plainAccountFacadeDelegate : P lainAccountFacadeDelegate

+ AccountFacadeImpl()

PlainAccountFacadeDelegate(from p lain)

<<use>>

es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi (2)

AccountFacadeImpl

Server

<<static>> + main(args : String[]) : void

S impleDat aSource(from sql)

DataSourceLocator(from sql)

<<instantiate>>

<<instantiate>> <<use>>

En un caso real, en vez de es.udc.fbellas.j2ee.util.sql.SimpleDataSource, se podría usar una implementación de javax.sql.DataSource que hiciese pool de conexiones (ej.: el pool de Struts)

es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi (y 3)

RMIAccountFacadeDelegate- accountFacade : AccountFacade

+ RMIAccountFacadeDelegate()

AccountFacadeDelegate(from delegat e)

<<Interface>>

AccountFacade<<Interface>><<use>>

ConfigurationParametersManager(from configuration)

<<use>>

Ahora los objetos Delegate y el Session Facade son distintos

El Delegate es un Proxy del Session Facade

es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi.RMIAccountFacadeDelegate (1)

public class RMIAccountFacadeDelegate implements AccountFacadeDelegate {

private final static String REGISTRY_ADDRESS_PARAMETER ="RMIAccountFacadeDelegate/registryAddress";

private AccountFacade accountFacade;

public RMIAccountFacadeDelegate() throws InternalErrorException { try {

String registryAddress = ConfigurationParametersManager.getParameter(

REGISTRY_ADDRESS_PARAMETER);String accountFacadeURL = "//" + registryAddress +

"/accountFacade";

accountFacade = (AccountFacade) Naming.lookup(accountFacadeURL);

} catch (Exception e) {throw new InternalErrorException(e);

}}

es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi.RMIAccountFacadeDelegate (y 2)

public AccountVO createAccount(AccountVO accountVO) throws InternalErrorException {

try {return accountFacade.createAccount(accountVO);

} catch (RemoteException e) {throw new InternalErrorException(e);

}

}

// Resto de operaciones => Idem “createAccount”.

} // class

Ejecución (1)

Servidor aplicaciones web RMIAccountFacade.sh BD

RMIMiniBank.war

Arquitectura en 3 capasRMIMiniBank.war: vista y controladorRMIAccountFacade.sh: modeloBase de datosNOTA: en general, la separación física entre modelo e interfaz gráfica (vista + controlador) sólo es positiva cuando hay aplicaciones cliente standalone (si la interfaz es web, como en este caso, es mejor que la capa modelo sea local)

Ejecución (y 2)

Clases en RMIMiniBank.war:WEB-INF/lib: jars de Standard TagLibs y Struts, StandardUtil.jar y WebUtil.jar (subsistema Util) y RMIAccountFacadeClient.jar

RMIAccountFacadeClient.jar: sólo las clases del modelo que precisa el cliente (interfaz de la fachada y clases relacionadas)

¡ No contiene DAOs ni la implementación de las operaciones de la fachada !

WEB-INF/classes: todas las clases del controlador y la vista

Clases en RMIAccountFacade.shDriver JDBC para la BDStandardUtil.jarRMIAccountFacade.jar: todas las clases del modelo


Recommended