Apartado 5.1: Introducción a las tecnologías de objetos

Anuncio
5.1 Introducción a las tecnologías de
objetos distribuidos con Java RMI
Contenidos
n
n
Tutorial de Java RMI
Caso de estudio: diseño e implementación de la capa
modelo de MiniBank con Java RMI
n
Arquitectura en 3 capas
Introducción
n
n
Java Remote Method Invocation (RMI)
Permite definir e implementar interfaces remotos en
el lenguaje Java
n
n
Solución de más alto nivel que el uso directo de
sockets o RPCs
n
n
Sus operaciones se pueden invocar remotamente, de la
misma forma que se invocan las operaciones de interfaces
locales
Facilidad de desarrollo
Apropiado para construir sistemas cliente/servidor en
una intranet, especialmente si cliente y servidor
están escritos en Java
n
RMI también puede funcionar sobre IIOP (CORBA)
Ejemplo Clock
TimeOfDay time = clock.getTimeOfDay()
Clock
Cliente
Servidor
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
n
n
Un interfaz remoto ha de extender, directa o
indirectamente, el interfaz java.rmi.Remote
Operaciones
n
n
n
Los tipos de los parámetros y del resultado han de ser
serializables
Han de declarar la excepción java.rmi.RemoteException
(además de las propias)
Paso de parámetros
n
n
Los objetos locales se pasan por valor (serializados)
Los objetos remotos se pasan por referencia
es.udc.fbellas.j2ee.rmitutorial.clock.server.ClockImpl
class ClockImpl extends UnicastRemoteObject implements Clock {
ClockImpl() throws RemoteException {}
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);
}
}
Implementación de interfaces remotos
n
Tipos de objetos remotos en Java RMI
n
n
n
Unicast remote object
n
n
n
Unicast remote objects
Activable objects
El servidor necesita estar constantemente en funcionamiento
Permanece siempre en memoria
Activable object
n
n
El servidor se activa la primera vez que se invoca uno de sus
métodos
El 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)
n Utilidad: implementación de servidores que gestionan
una gran cantidad de objetos
Implementación de Unicast Remote Objects
n
La clase de implementación extiende
java.rmi.server.UnicastRemoteObject e
n
implementa el interfaz remoto
Para que el objeto pueda ser accedido remotamente,
necesita ser exportado
n
El constructor sin argumentos de UnicastRemoteObject
exporta el objeto y puede lanzar
java.rmi.RemoteException
n
El constructor (con o sin argumentos) de la clase de
implementación (que invoca automáticamente al constructor
sin argumentos de la clase padre) necesita declarar la
excepción java.rmi.RemoteException
Stubs y skeletons (1)
Resto aplicación
cliente
getTimeOfDay
<<interface>>
Clock
<<interface>>
java.rmi.server.Skeleton
ClockImpl_Stub
getTimeOfDay
ClockImpl_Skel
ClockImpl
dispatch
Subsistema RMI
Cliente
Subsistema RMI
Servidor
Stubs y skeletons (2)
n
Stub
n
n
n
Clase usada por el cliente en sustitución de la remota
Implementa 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
n
n
n
En términos de patrones, un stub es un Proxy
Skeleton
n
n
n
n
Los parámetros y el valor de retorno se envían serializados
Clase usada por el servidor
Recibe los mensajes, invoca la operación del objeto que
implementa el interfaz remoto y envía el resultado al
llamador
En términos de patrones, un skeleton es un Adapter
Los Stubs y skeletons son transparentes al código
Stubs y skeletons (y 3)
n
n
Cuando un cliente invoca una operación remota que
devuelve una referencia a un objeto remoto, obtiene
una instancia del stub correspondiente
Generación de stubs y skeletons
rmic es.udc.fbellas.j2ee.rmitutorial.clock.server.ClockImpl
n
Genera
n
n
es.udc.fbellas.j2ee.rmitutorial.clock.server.ClockImpl_Stub
es.udc.fbellas.j2ee.rmitutorial.clock.server.ClockImpl_Skel
Servicio de nombres
n
Problema
n
n
El servidor crea un objeto que implementa el interfaz Clock,
¿ Cómo obtienen los clientes una referencia a ese objeto ?
Servicio de nombres
n
n
n
n
Permite asociar nombres lógicos (ej.: clock) a objetos
Servidor: asocia un nombre a un objeto
Cliente: obtiene una referencia al objeto a partir del nombre
Objetivo: independencia de ubicación
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
c = sn.lookup(“clock”)
Cliente
sn.bind(“clock”, c)
Servicio de
nombres
Servidor
El servicio de nombres de Java RMI
n
n
Java RMI define un servicio de nombres muy sencillo
El esquema de nombrado sigue la sintaxis de una
URL
n
n
//máquina:puerto/nombreDeObjeto, siendo
nombreDeObjeto un nombre simple (ej.: clock)
n
máquina y puerto hacen referencia a la máquina en la que
n
corre el servicio de nombres (y no el objeto remoto)
Por defecto, máquina = localhost y puerto = 1099
Interfaz java.rmi.registry.Registry
n
Permite asociar nombres simples a objetos
java.rmi.registry.Registry
public 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
n
n
Aplicación que contiene un objeto que implementa el
interfaz java.rmi.registry.Registry
No es persistente
Por 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
c = r.lookup(“clock”)
Registry
n
Cliente
r.bind(“clock”, c)
Servidor
rmiregistry
Máquina A
Máquina B
java.rmi.Naming (1)
n
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
n
Ejemplo:
Clock clock = (Clock) Naming.lookup(
“//knopfler/clock”);
n
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.Server
class Server {
public static void main (String[] args) {
/* Install RMI security manager. */
System.setSecurityManager(new RMISecurityManager());
/*
* Create an instance of "ClockImpl", and register it in the
* RMI registry.
*/
try {
ClockImpl clock = new ClockImpl();
Naming.rebind("clock", clock);
System.out.println("Server is working ...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
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];
}
try {
/* Install RMI security manager. */
System.setSecurityManager(new RMISecurityManager());
es.udc.fbellas.j2ee.rmitutorial.clock.client.Client (y 2)
/* 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
n
No cumple la propiedad de independencia de
ubicación
n
La URL del objeto lleva el nombre y puerto de la máquina en
la que corre el rmiregistry
n
n
Si el rmiregistry cambia de máquina o puerto, es necesario
recompilar los clientes
Alternativamente, 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
n
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
n
No es escalable
Política Multi-thread
n
Invocaciones desde distintas máquinas virtuales
sobre un mismo objeto
n
n
Invocaciones desde una misma máquina virtual sobre
un objeto remoto
n
n
Se crea un thread por cada invocación en el servidor
(“thread-per-request”)
No se garantiza que se cree un thread por cada invocación
en el servidor
Conclusión
n
La clase que implementa un interfaz remoto necesita ser
thread-safe
Ejecución (1)
n
ClockServer.jar
n
n
n
ClockClient.jar
n
n
n
es.udc.fbellas.j2ee.rmitutorial.clock.client.*
es.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface.*
ClockRMIInterface.jar
n
n
es.udc.fbellas.j2ee.rmitutorial.clock.server.*
es.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface.*
es.udc.fbellas.j2ee.rmitutorial.clock.rmiinterface.*
ClockRMIInterfaceStub.jar
n
es.udc.fbellas.j2ee.rmitutorial.clock.server.ClockImpl_S
tub
Ejecución – ClockServer.sh (2)
#!/bin/sh
# Class path.
CP=$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\
ClockServer.jar
# Code base 1 (file system).
CODE_BASE="\
file:$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\
ClockRMIInterface.jar \
file:$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\
ClockRMIInterfaceStub.jar"
# Code base 2 (web server).
#CODE_BASE="\
#http://knopfler/fbellas/ClockRMIInterface.jar \
#http://knopfler/fbellas/ClockRMIInterfaceStub.jar"
# Start server.
java -classpath $CP -Djava.security.policy=policy \
-Djava.rmi.server.codebase="$CODE_BASE" \
es.udc.fbellas.j2ee.rmitutorial.clock.server.Server
Ejecución – ClockClient.sh (3)
#!/bin/sh
#
#
#
#
--------------------------------------------------------------------Optionally can receive the RMI registry address (with the format
host[:port]).
---------------------------------------------------------------------
# Class path 1 (with remote interface stub).
CP=$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\
ClockClient.jar:$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\
ClockRMIInterfaceStub.jar
# Class path 2 (without remote interface stub).
#CP=$J2EE_EXAMPLES_HOME/Subsystems/RMITutorial/Build/Jars/\
#ClockClient.jar
# Start client.
java -classpath $CP -Djava.security.policy=policy \
es.udc.fbellas.j2ee.rmitutorial.clock.client.Client "$@"
Ejecución – “class path 1” y “code base 1” (4)
Máquina A
ClockClient.sh
Máquina B
3: c = Naming.lookup(“clock”)
4: t = c.getTimeOfDay()
1: Naming.bind(“clock”, c)
rmiregistry
ClockServer.sh
2: Carga ClockImpl_Stub del
codebase del servidor
n
n
El class path del cliente incluye ClockImpl_Stub
Problemas
n
n
Si la implementación de ClockImpl cambia es necesario
generar otra vez el stub y actualizar los clientes
Si existen varias implementaciones de Clock, es necesario
incluir todos los stubs en todos los clientes
Ejecución – “class path 2” y “code base 1” (5)
Máquina A
Máquina B
3: c = Naming.lookup(“clock”)
4: Carga ClockImpl_Stub del
codebase del servidor
ClockClient.sh
5: t = c.getTimeOfDay()
1: Naming.bind(“clock”, c)
rmiregistry
ClockServer.sh
2: Carga ClockImpl_Stub del
codebase del servidor
n
n
El class path del cliente no incluye ClockImpl_Stub
Problemas
n
Cliente y servidor necesitan compartir el sistema de ficheros en el
que residen los ficheros .jar especificados en el codebase
n
Cliente y servidor en la misma máquina o compartiendo sistema de
ficheros vía NFS o similar
Ejecución – “class path 2” y “code base 2” (6)
Máquina A
ClockClient.sh
Máquina B
3: c = Naming.lookup(“clock”)
rmiregistry
1: Naming.bind(“clock”, c)
5: t = c.getTimeOfDay()
ClockServer.sh
2: Carga ClockImpl_Stub del
servidor web
4: Carga ClockImpl_Stub
del servidor web
Servidor web
n
El codebase reside en un servidor web (o de FTP)
Ejecución (y 7)
n
Cuando el cliente no tiene el stub en su class path
(dos últimas soluciones)
n
Es preciso emplear un gestor de seguridad
(RMISecurityManager)
n
n
El modelo de ejecución de Java requiere usar un gestor de
seguridad para todas las clases que se cargen al margen del
class path local
Es preciso especificar un fichero de políticas. Ejemplo:
grant {
permission java.security.AllPermission;
};
n
En un entorno no controlado (ej.: Internet) esta política sería
peligrosa, pero no en uno controlado (ej.: la intranet de una
empresa)
es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi (1)
<<Interface>>
Remote
(from rmi)
<<Interface>>
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) : Collection
+ removeAccount(accountIdentifier : Long) : void
+ transfer(sourceAccountIdentifier : Long, destinationAccountIdentifier : Long, amount : double) : void
+ findAccountOperationsByDate(accountIdentifier : Long, startDate : Calendar, endDate : Calendar, startIndex : int, count : int) : Collection
AccountFacadeImpl
- plainAccountFacadeDelegate : PlainAccountFacadeDelegate
+ AccountFacadeImpl()
<<use>>
PlainAccountFacadeDelegate
(from plain)
es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi (2)
AccountFacadeImpl
<<instantiate>>
Server
<<static>> + main(args : String[]) : void
<<instantiate>>
SimpleDataSource
(from sql)
n
<<use>>
DataSourceLocator
(from sql)
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)
<<Interface>>
AccountFacadeDelegate
(from delegate)
RMIAccountFacadeDelegate
- accountFacade : AccountFacade
<<use>>
<<Interface>>
AccountFacade
+ RMIAccountFacadeDelegate()
<<use>>
ConfigurationParametersManager
(from configuration)
n
Ahora los objetos Delegate y el Session Facade son
distintos
n
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;
static {
/* Install RMI security manager if needed. */
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
}
es.udc.fbellas.j2ee.minibank.model.accountfacade.rmi.RMIAccountFacadeDelegate (2)
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 3)
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
RMIMiniBank.war
n
Arquitectura en 3 capas
n
n
n
RMIMiniBank.war: vista y controlador
RMIAccountFacade.sh: modelo
Base de datos
BD
Ejecución (y 2)
n
Clases en RMIMiniBank.war:
n
WEB-INF/lib: jars de Standard TagLibs y Struts,
StandardUtil.jar y WebUtil.jar (subsistema Util),
RMIAccountFacadeClient.jar y
RMIAccountFacadeClientStub.jar
n
RMIAccountFacadeClient.jar: sólo las clases del modelo
que precisa el cliente (interfaz de la fachada y clases
relacionadas)
n
n
n
n
¡ No contiene DAOs ni la implementación de las operaciones de la
fachada !
RMIAccountFacadeClientStub.jar: el stub del interfaz
remoto (se puede eliminar si se desear cargar dinámicamente)
WEB-INF/classes: todas las clases del controlador y la
vista
Clases en RMIAccountFacade.sh
n
n
n
Driver JDBC para la BD
StandardUtil.jar
RMIAccountFacade.jar: todas las clases del modelo
Descargar