0% encontró este documento útil (0 votos)
270 vistas58 páginas

Java RMI

Este documento introduce el mecanismo de invocación remota (RMI) de Java. Explica que RMI permite hacer llamadas a métodos de objetos remotos de forma transparente. Describe algunas de las características clave de RMI, como la serialización de datos, la localización y activación de objetos remotos, y la gestión de excepciones. Finalmente, presenta un caso práctico de uso de RMI para desarrollar una calculadora remota.

Cargado por

Luigui Espinosa
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
270 vistas58 páginas

Java RMI

Este documento introduce el mecanismo de invocación remota (RMI) de Java. Explica que RMI permite hacer llamadas a métodos de objetos remotos de forma transparente. Describe algunas de las características clave de RMI, como la serialización de datos, la localización y activación de objetos remotos, y la gestión de excepciones. Finalmente, presenta un caso práctico de uso de RMI para desarrollar una calculadora remota.

Cargado por

Luigui Espinosa
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 58

Java RMI

Santi Caballé Llobet


PID_00187406
CC-BY-SA • PID_00187406 Java RMI

Los textos e imágenes publicados en esta obra están sujetos –excepto que se indique lo contrario– a una licencia de
Reconocimiento-Compartir igual (BY-SA) v.3.0 España de Creative Commons. Se puede modificar la obra, reproducirla, distribuirla
o comunicarla públicamente siempre que se cite el autor y la fuente (FUOC. Fundació per a la Universitat Oberta de Catalunya), y
siempre que la obra derivada quede sujeta a la misma licencia que el material original. La licencia completa se puede consultar en:
http://creativecommons.org/licenses/by-sa/3.0/es/legalcode.ca
CC-BY-SA • PID_00187406 Java RMI

Índice

Introducción............................................................................................... 5

Objetivos....................................................................................................... 6

1. Introducción a RMI........................................................................... 7
1.1. Qué es RMI .................................................................................. 7
1.2. Objetivos de RMI ........................................................................ 8
1.3. Características RMI ...................................................................... 9
1.3.1. Serialización ................................................................... 10
1.3.2. Paso de parámetros y valores de retorno ....................... 19
1.3.3. Localización ................................................................... 21
1.3.4. Activación de objetos remotos ...................................... 22
1.3.5. Recolector de basura distribuido ................................... 24
1.3.6. Excepciones .................................................................... 26
1.3.7. Seguridad ........................................................................ 27

2. Arquitectura RMI............................................................................... 29
2.1. El servidor ................................................................................... 30
2.2. El cliente ...................................................................................... 31
2.3. Stubs y skeletons............................................................................ 32
2.3.1. Evolución de los stubs y skeletons en Java ..................... 33
2.3.2. Proceso de invocación cliente/servidor ......................... 33
2.4. RMIRegistry.................................................................................... 34

3. Caso de estudio RMI.......................................................................... 36


3.1. Un ejemplo de RMI básico: una calculadora remota .................. 36
3.1.1. Desarrollar el objeto remoto ......................................... 37
3.1.2. Creación del archivo de políticas .................................. 39
3.1.3. Desarrollo del cliente ..................................................... 40
3.2. Compilar y ejecutar .................................................................... 43
3.2.1. Compilar la interfaz remota, servidor y cliente ............. 43
3.2.2. Generar stubs y skeletons con rmic.................................. 43
3.2.3. Arrancar el registro (RMIRegistry) ................................... 44
3.2.4. Ejecutar el servidor ........................................................ 44
3.2.5. Ejecutar el cliente .......................................................... 45
3.3. Automatización de tareas ............................................................ 46
3.4. Ejecución en un entorno distribuido ......................................... 47

Resumen....................................................................................................... 49

Actividades.................................................................................................. 51
CC-BY-SA • PID_00187406 Java RMI

Ejercicios de autoevaluación.................................................................. 51

Solucionario................................................................................................ 53

Glosario........................................................................................................ 55

Bibliografía................................................................................................. 57
CC-BY-SA • PID_00187406 5 Java RMI

Introducción

En este módulo estudiaremos el mecanismo de invocación remota RMI de Ja- Ved también
va. Tal como hemos visto en el módulo "Introducción a las plataformas dis-
Trataremos la tecnología de
tribuidas", Java sitúa RMI en el centro de su modelo de objetos distribuido y componentes Java en el módu-
representa la capa subyacente de comunicación distribuida de la tecnología lo "Java EE".

de componentes.

Veremos cómo, a pesar de la simplicidad y limitaciones de este mecanismo,


entender RMI es fundamental para disponer de un buen conocimiento de la
comunicación distribuida interna de Java. Dicho conocimiento nos permitirá
entender el funcionamiento de tecnologías distribuidas más complejas.

A lo largo del módulo presentamos, primero, las características principales de


RMI para el desarrollo de aplicaciones distribuidas en Java. Entre tales caracte-
rísticas, explicaremos en detalle el protocolo de comunicación distribuida de
Java basado en el mecanismo de la serialización de datos.

A continuación, presentaremos la arquitectura RMI y describiremos cada uno


de sus elementos, lo que nos proporcionará una visión de su funcionamiento
interno. Veremos que, a partir de la simplicidad y la transparencia de RMI, así
como de la fidelidad de este mecanismo de invocación remota a la programa-
ción orientada a objetos, no se perciben grandes diferencias al pasar de pro-
gramar en Java aplicaciones de escritorio o locales a aplicaciones distribuidas.

Finalmente, propondremos un caso de estudio de aplicación distribuida desa-


rrollado con RMI. A pesar de la simplicidad del caso, nos permitirá afianzar en
la práctica los conceptos de esta tecnología trabajados hasta el momento.
CC-BY-SA • PID_00187406 6 Java RMI

Objetivos

Este módulo os permitirá consolidar los siguientes objetivos:

1. Entender qué es el método de invocación remota (RMI) de Java.

2. Entender las ventajas y las limitaciones de RMI comparado con otros pa-
radigmas de programación distribuida.

3. Identificar las características internas de RMI.

4. Conocer la arquitectura y el funcionamiento interno de RMI.

5. Conocer alguna aplicación práctica de RMI.


CC-BY-SA • PID_00187406 7 Java RMI

1. Introducción a RMI

(1)
En este apartado introduciremos el mecanismo de invocación remota de Java, Del inglés remote method invoca-
1 tion.
llamado RMI . Veremos sus características intrínsecas y objetivos esenciales,
que convierten esta tecnología en una de las más simples de usar para construir
Un poco de historia
aplicaciones distribuidas.
RMI forma parte de Java des-
de la versión 1.1 del Java de-
Entraremos a fondo en las características más importantes de RMI, que con- velopment kit (JDK), liberado
en 1997, y fue desarrollado en
sigue hacer transparente al desarrollador de aplicaciones distribuidas la com- la empresa Sun Microsystems.
plejidad interna de la red, tanto en la codificación de los datos por transmitir RMI fue revisado y se le intro-
dujeron importantes mejoras a
como en la gestión de las conexiones remotas. Esta transparencia representa partir del JDK 1.5, liberado en
el 2004, mejoras que perduran
la piedra angular del soporte RMI a las aplicaciones distribuidas. en nuestros días. En enero del
2010, la empresa Oracle ad-
quirió Sun Microsystems, y a
A partir del conocimiento adquirido en este apartado, estaremos en disposi- partir de ese momento adqui-
rió todos los derechos del len-
ción de tomar decisiones durante el desarrollo de nuestras aplicaciones distri- guaje Java, incluido RMI.
buidas y, de este modo, potenciar la capacidad distribuida.

Nota
1.1. Qué es RMI
Una JVM (Java virtual machine,
en inglés) es una máquina vir-
El mecanismo de invocación remota de Java, RMI, permite invocar métodos tual ejecutable en diferentes
de objetos que se encuentran en JVM diferentes del objeto que invoca (invo- plataformas, capaz de interpre-
tar y ejecutar instrucciones de
cación� remota) siguiendo el mismo procedimiento que una invocación de código binario especial (el by-
tecode Java), que es generado
métodos de objetos que se encuentran en la misma máquina y JVM (invoca- por el compilador del lenguaje
Java.
ción�local). Una invocación remota implica comunicar procesos separados si-
tuados en una misma máquina o en máquinas separadas situadas en diferen-
tes puntos geográficos.

CORBA
RMI representa el modelo de objetos distribuidos que propone Java co-
mo solución para desarrollar aplicaciones distribuidas. Java también da soporte al
modelo de objetos distribui-
dos de CORBA, mediante RMI-
IIOP. Repasad estos conceptos
en el módulo "Introducción a
En el contexto de una invocación remota, el objeto que invoca se denomina las plataformas distribuidas"
de este material didáctico. Sin
cliente, mientras que el objeto remoto, cuyos métodos se están invocando, se
embargo, en este módulo no
denomina servidor. En el caso general, el objeto local que efectúa la invoca- prestamos atención al modelo
CORBA desde Java, ya que tie-
ción interpreta el rol de cliente invocando los métodos de un objeto remoto ne un uso marginal.
que interpreta el rol de servidor. Aun así, a veces el objeto local y el remoto
pueden intercambiar sus roles de forma dinámica (por ejemplo, durante las
Nota
notificaciones y avisos al cliente, es el servidor el que inicia la interacción para
informar al cliente). Fijaos en que la respuesta del
servidor a la invocación de un
cliente no comporta ningún
cambio de rol, al estar la res-
Durante una invocación local (dentro de una misma JVM), un objeto local puesta del servidor incluida en
hace referencia directa a otro objeto local para invocar sus métodos. En con- el proceso mismo de invoca-
ción del cliente.
traposición, en una invocación remota, el objeto cliente nunca hace referencia
CC-BY-SA • PID_00187406 8 Java RMI

directamente al objeto servidor, sino a una interfaz remota, la cual establece Ved también
una clara separación entre la definición y la implementación del objeto servi-
Explicaremos el funcionamien-
dor. De este modo, se establece una separación entre las firmas de los métodos to interno de RMI en detalle en
y el código de dichos métodos. el apartado "Arquitectura RMI"
de este módulo.

Esto último permite que la definición y la implementación estén en JVM se-


paradas. Esta separación encaja con las necesidades de un sistema distribuido
típico, en el que el cliente solo está interesado en la definición del servicio
(interfaz) que solicita al servidor, el cual es responsable de tener implementa-
do dicho servicio para proporcionarlo al cliente.

En la parte negativa, RMI ha sido criticado por utilizar un protocolo de comu- Ved también
nicación restrictivo conocido como Java remote method protocol (JRMP). Este
Repasad el protocolo JRMP en
protocolo, al estar implementado en Java, obliga a que cliente y servidor es- el módulo "Introducción a las
tén implementados en Java. Esta restricción resta flexibilidad al modelo RMI plataformas distribuidas". En
este módulo damos por asumi-
en relación con otras tecnologías que admiten distintas tecnologías de imple- do que, cuando hablamos de
la comunicación RMI, se reco-
mentación. noce el protocolo de comuni-
cación JRMP.

La figura 1 ejemplifica de forma gráfica el comportamiento general de un ser-


vicio distribuido en RMI. En una calculadora distribuida, el cliente solicita
efectuar una operación de suma de dos operandos enteros, mientras que el
servidor realiza dicha operación y devuelve el resultado al cliente. El cliente
solo ve la definición del servicio y la utiliza para solicitar la operación de suma.
Esta definición del servicio se corresponde con la firma del método public
int sumar(int a, int b);. El servidor se encarga de la implementación
de este servicio proporcionando el código del método de suma.

Figura 1

1.2. Objetivos de RMI

Cuando se desarrolló RMI, se planteó como una solución para entornos exclu-
sivos Java con la finalidad última de simplificar el desarrollo de aplicaciones
distribuidas orientadas a objetos y escritas en Java. Se quería que RMI propor-
CC-BY-SA • PID_00187406 9 Java RMI

cionara los mecanismos necesarios para hacer transparente la complejidad de


la comunicación en red, con lo que se consiguiría que el programador pudiese
concentrarse en la lógica particular de la aplicación que desarrolla.

Para lograr tales retos, los creadores de RMI se fijaron los objetivos siguientes:

• Transparencia. Permitir la invocación de métodos de objetos remotos que


se ejecutan en otras JVM como procesos separados, con independencia de
que estos objetos estén en la misma máquina o en máquinas diferentes.

• Orientado�a�objetos. Integrar el modelo de objetos distribuidos en el len-


guaje Java de manera natural y preservando, siempre que sea posible, la
semántica de objetos de Java.

• Simple. Aplicar a la programación distribuida los mismos criterios de sim-


plicidad de la programación Java estándar.

• Seguridad. Usar un modelo de seguridad basado en los gestores de segu-


ridad y cargadores de clases locales.

• Portabilidad. Hacer que cualquier sistema distribuido RMI sea portable a


cualquier plataforma donde haya una JVM.

• Escalabilidad. Mantener la misma arquitectura con independencia del ta-


maño de la aplicación distribuida.

• Robustez. Proporcionar una jerarquía de gestión de excepciones remotas


para detectar y gestionar los errores generados en el servidor.

• Eficiencia. Permitir la activación, desactivación y reactivación, así como


la liberación automática de objetos remotos no referenciados, lo que me-
joraría la gestión de recursos del servidor.

En los apartados siguientes veremos con detalle las características que ofrece
RMI a la programación distribuida, con las que consigue lograr los objetivos
expuestos.

1.3. Características RMI

RMI es una solución cliente-servidor basada en el conocido protocolo RPC, Ved también
al que añade el paradigma de la programación orientada a objetos y permite
Revisad el protocolo remote
la comunicación entre objetos remotos que se encuentran en JVM separadas. procedure call (RPC) en la asig-
RMI está construido sobre la base siguiente: natura Redes y aplicaciones In-
ternet.

• El modelo orientado a objetos de Java.


• El soporte Java a la programación por sockets en redes TCP/IP.
CC-BY-SA • PID_00187406 10 Java RMI

• El mecanismo de serialización de objetos de Java.

Los puntos primero y segundo, el modelo orientado a objetos de Java y la Ved también
programación por sockets, ya han sido tratados en asignaturas anteriores. El
Revisad la orientación a obje-
tercer punto clave, el mecanismo de la serialización, se tratará a continuación tos de Java en la asignatura Di-
de forma detallada. seño y programación orientada
a objetos, y la programación
por sockets, en la asignatura
Redes y aplicaciones Internet.
También analizaremos otras características importantes de RMI, como el paso
de parámetros, el proceso de activación de objetos, el recolector de basura dis-
tribuido, el proceso de localización de objetos remotos, el uso de excepciones
en RMI y el soporte a la seguridad.

Java ofrece de manera transparente las características RMI como parte


de la implementación de su modelo de objetos distribuidos.

Gracias a dicha transparencia, el programador no tendrá que implicarse en Escalabilidad de la


estas tareas, a menos que tenga requisitos de eficiencia críticos, entre otras aplicación

razones. En estos casos, deberá escoger la opción "manual" que Java siempre La escalabilidad de la aplica-
pone a disposición del programador e implementar estas características con su ción puede ser otra razón por
la que no se puedan utilizar las
propio código. Aun así, para la mayoría de los casos, al igual que para los que opciones RMI por defecto.
veremos en esta asignatura, se utilizará la implementación RMI por defecto
que proporciona Java.

1.3.1. Serialización

La serialización de objetos es la acción de codificar un objeto en una


secuencia de datos.

Más concretamente, el mecanismo de serialización que proporciona RMI con- Serialización y


siste en convertir clases de objetos en una secuencia de bytes. Si invertimos deserialización

este proceso, obtendremos una copia del objeto original a partir de estos mis- El proceso de serialización
mos bytes de datos, y haremos lo que se conoce como deserialización. también se conoce como
marshalling, mientras que la
deserialización se conoce co-
mo unmarshalling.
Estos procesos se llevan a cabo sin apenas intervención del programador, lo
cual proporciona un elevado grado de transparencia, gracias a la abstracción
de los streams que proporciona Java, que representan un flujo o secuencia de Nota
bytes. Los streams permiten que dos programas se intercambien datos en forma
Los streams de Java se explican
de secuencia de bytes. Dichos programas pueden estar ejecutándose en dos con detalle a continuación.
máquinas o sistemas de comunicación diferentes y estar conectados en red
mediante otra abstracción muy importante, los sockets.
CC-BY-SA • PID_00187406 11 Java RMI

En RMI, los datos (objetos) que pasamos por parámetro durante la invo-
cación de un método remoto son serializados y convertidos en un stream
de bytes por el proceso que invoca. Este proceso escribe en un socket que
retransmite el stream por la red hasta llegar al socket de destino.

El proceso inverso es simétrico. El proceso remoto lee del socket y deserializa


el stream. Los datos deserializados serán una copia del dato original, que se
utilizará como parámetro en el método remoto.

Figura 2

En la figura 2 vemos la relación entre RMI, sockets y streams durante una co-
nexión típica de red cliente/servidor. RMI abstrae la funcionalidad de los soc-
kets, que a su vez usan streams para enviar secuencias de datos entre programas
durante una comunicación en red a bajo nivel. Las flechas punteadas bidirec-
cionales representan la comunicación lógica en ambas direcciones. La capa
de transporte y red TCP/IP representa la comunicación activa entre cliente y
servidor.

Aunque ni los sockets ni los streams forman parte directamente de RMI, para Ved también
comprender el funcionamiento de la comunicación en red de Java y el meca-
La comprensión de los sockets
nismo de serialización de RMI, primero es preciso comprender estos dos ele- y de la comunicación en red
mentos. A continuación describimos los streams. de Java queda fuera del alcan-
ce de esta asignatura. Revisad
tales conceptos en la asignatu-
ra Redes y aplicaciones Internet.
CC-BY-SA • PID_00187406 12 Java RMI

Streams
Nota

Por comodidad, en este módu-


lo utilizaremos la nomenclatu-
Los streams son una estructura de datos que permite almacenar y recu- ra inglesa de streams en lugar
perar información de manera inevitablemente secuencial. de flujo o secuencia de bytes.

Los datos (típicamente, bytes) solo se pueden añadir al stream uno por uno,
y solo se recuperan del mismo modo. Así pues, es preciso empezar a recorrer
la secuencia siempre desde el inicio hasta el final. No es posible recorrer la
secuencia de otro modo (ir hacia atrás, saltar sitios o empezar en medio). Una
vez recuperado un dato del stream, hay que moverse a la posición siguiente de
la secuencia y, una vez escrito un dato, no se puede borrar.

Aunque puede parecer que esta forma de tratar la información es pesada y poco
útil, los streams presentan una característica muy importante: la simplicidad.
El acceso secuencial a la información hace que los streams sean compatibles
con cualquier dispositivo, ya sea físico (por ejemplo, una impresora) o abstrac-
to (por ejemplo, un archivo). Esta simplicidad permite uniformar el acceso al
dispositivo por medio de un proceso trivial: primero, se asocia un stream a un
dispositivo y, después, la información es leída o escrita secuencialmente en
dicho dispositivo por medio del stream.

Las secuencias son una primitiva para representar información, y toda forma Ved también
más compleja de representación se puede ver como una aplicación recursiva de
Repasad las secuencias como
estas. Por ejemplo, para manejar bloques de diez datos, primero se recuperan tipos de datos en la asignatura
diez datos consecutivos de una secuencia y después se crea un bloque con ellos. Diseño de estructuras de datos.

De este modo, se consigue trabajar con estructuras más complejas a partir de


un stream primitivo.

Java ofrece dos clases principales para trabajar con streams: OutputS-
tream e InputStream.

OutputStream es una clase abstracta que representa un flujo de bytes de sa- Dispositivo de salida
lida. Un output stream acepta bytes de salida, que escribe en un dispositivo
Un dispositivo de salida se
genérico de salida. El método más importante de esta clase es write(byte), considera apropiado mientras
que va escribiendo en el dispositivo un byte cada vez que es llamado. Las sub- sea capaz de recibir o almace-
nar los datos que le llegan de
clases de OutputStream implementan la salida de bytes en dispositivos espe- un OutputStream (por ejem-
plo, archivos, pipes, servidores,
cíficos (por ejemplo, FileOutputStream y PipeOutputStream involucran etc.).
archivos y pipes, respectivamente).
CC-BY-SA • PID_00187406 13 Java RMI

De forma simétrica, InputStream es una clase abstracta que representa un Dispositivo de entrada
flujo de datos de entrada. Un input stream acepta bytes de entrada, que lee de
En este caso, un dispositivo
un dispositivo genérico de entrada. El método más importante de esta clase de entrada se considera apro-
es read(), que recupera un byte del dispositivo cada vez que es llamado. Las piado si puede trabajar como
fuente de datos y coincide con
subclases de InputStream implementan la entrada de bytes desde dispositi- los dispositivos de salida que
hemos visto.
vos específicos.

InputStream y OutputStream ofrecen otros métodos de lectura y escritura Combinar streams


que permiten trabajar con bloques de bytes en vez de tratar los bytes de forma
Consultad en el paquete
individual. read(byte[]) y write(byte[]) son dos métodos que ofrecen java.io de la API de Java
esta característica. Sin embargo, internamente los bytes siempre son tratados otras maneras de combinar los
streams con bloques de datos,
uno por uno de manera secuencial. junto con el resto de los méto-
dos disponibles en InputS-
tream y OutputStream.

Un stream puede envolver a otros streams de forma recursiva para con-


seguir una funcionalidad incremental.

A partir de esta idea, hay dos tipos de streams:

1)�Streams�primitivos. Son los que hablan con los dispositivos directamen-


te leyendo o escribiendo los bytes tal como llegan de manera secuencial. Es-
tos streams son los que hemos visto hasta ahora (por ejemplo, FileOutputS-
tream y FileInputStream).

2)�Streams�intermedios. Estos nunca hablan directamente con los dispositi- Ejemplo de streams
vos. Su función es la de envolver un stream existente (puede ser un stream intermedios

primitivo o intermedio) para ganar en funcionalidad. Hablamos entonces de Un ejemplo de flujos interme-
streams�contenedores y streams�contenidos. Los propósitos más habituales dios son las clases Buffere-
dOutputStream y Buffere-
de los streams intermedios son para tareas como el almacenamiento en la me- dInputStream, muy emplea-
das para proporcionar capaci-
moria intermedia, pipeling, y compresión y serialización de datos. dad de almacenamiento tem-
poral (buffering).

Normalmente, el stream contenedor tiene el stream contenido como paráme-


tro (lo envuelve), y así le añade la funcionalidad del contenedor. Los streams
intermedios pueden manejar datos más complejos que simples bytes, pero se
tienen que convertir en bytes cuando escriban en el dispositivo. La lógica (ope-
raciones) del stream más externo se irá propagando por todos los streams hasta
llegar al más interno (stream primitivo), que leerá o escribirá en bytes en el
dispositivo.

Durante la serialización, los streams intermedios más utilizados son las


clases ObjectOutputStream y ObjectInputStream, que convierten
objetos en bytes y a la inversa.
CC-BY-SA • PID_00187406 14 Java RMI

Los principales métodos de estas dos clases son writeObject(Object), que


representa el mecanismo de serialización en esencia (traduce objetos en se-
cuencias de datos de tipo byte), y el método Object readObject(), que re-
presenta el mecanismo inverso (deserialización).

Mecánica de la serialización

El proceso de serializar un objeto se consigue en dos pasos:

1) Crear un objeto ObjectOutputStream.

2) Llamar al método writeObject(Object) pasando por parámetro el objeto


por serializar; este convertirá el objeto en bytes y lo escribirá en el stream que
se haya pasado por parámetro al constructor de ObjectOutputStream.

Para deserializar se sigue un proceso simétrico:

1) Crear un objeto ObjectInputStream.

2) Llamar al método Object readObject(), que construirá un objeto a partir


de los bytes del stream pasado al constructor de ObjectInputStream.

Dado que ObjectOutputStream y ObjectInputStream proporcionan el Nota


comportamiento por defecto de toda serialización, RMI no las utiliza por ra-
Para la comprensión de este
zones de eficiencia. En su lugar, crea subclases de estas en las que redefine al- apartado, basta con conocer
gunos métodos protegidos de las superclases. aquellas clases que hacen posi-
ble el mecanismo de serializa-
ción por defecto.

Un caso especial son las aplicaciones con grandes jerarquías de herencia, en


las que la serialización no se debería aplicar en las superclases (especialmente
clases abstractas e interfaces), sino solo en las subclases con las que trabajamos
realmente. De lo contrario, tendremos que controlar de forma minuciosa y
evitar la propagación de la serialización de la superclase a aquellas subclases o
ciertos atributos de estas que no queramos serializar.

Para los casos especiales, se suelen manejar los atributos de la superclase en las
subclases mediante la redefinición de los métodos writeObject() y readOb-
ject() que hemos visto. Sin embargo, para simplificar y evitar la serialización
manual, en los casos más habituales es mejor serializar las superclases.

Usos de la serialización

En Java, el algoritmo de serialización que convierte objetos en streams de bytes


es único. El proceso de conversión siempre es el mismo, pero, en cambio, los
usos potenciales que se pueden hacer de este proceso son múltiples.
CC-BY-SA • PID_00187406 15 Java RMI

Por ejemplo, si asignamos un FileOutputStream a un archivo, los datos que


escribamos en el stream quedarán almacenados de manera persistente en dicho
archivo. De este modo, se puede crear un auténtico mecanismo de persistencia.

También podemos utilizar la serialización como un mecanismo de copia de


objetos en memoria mediante el uso de ByteArrayOutputStream, gracias al
cual los datos escritos en el stream se guardan temporalmente en una matriz
de tipo byte. De este modo, mediante matrices de bytes, podemos hacer copias
de un objeto original en la memoria temporal del ordenador.

Para los propósitos de RMI, el principal uso de la serialización es como meca-


nismo de comunicación. Si escribimos datos en un stream asignado a un soc-
ket, dichos datos serán transmitidos automáticamente por la red hasta el soc-
ket de destino, donde otro proceso los recuperará mediante el correspondiente
stream de lectura y se encargará de ellos.

Identificador universal de serialización

Las clases que serializamos para enviar por la red, que después son cargadas Nota
tanto en el cliente como en el servidor, deben ser las mismas versiones.
Imaginemos los problemas
que tendríamos si cliente y ser-
vidor trabajaran con versiones
diferentes de las mismas clases
Java garantiza la unicidad de las clases mediante la generación en tiem- y en una se hubieran hecho
po de compilación de un identificador de versión llamado identifica- modificaciones.

dor�universal�de�versión o serialVersionUID, que genera un identificador


diferente cada vez que se modifica una clase.

Este identificador se añade a la clase que queremos serializar de modo que


cliente y servidor puedan saber si trabajan con las mismas versiones de la clase.
En caso de no ser así, se lanza una excepción del tipo InvalidClassExcep-
tion para indicar que las clases serializables que utilizan cliente y servidor no
son las mismas internamente.

Sin embargo, este identificador que genera Java por defecto es altamente sen-
sible al tipo de compilador que estamos utilizando y puede generar diferentes
identificadores aunque no se hayan hecho modificaciones en la clase. Para
evitar este problema, se recomienda generar un identificador explícitamente
dentro del código de la clase. Esto evitará que Java genere su identificador pro-
pio por defecto y nos aseguramos de que es el mismo identificador, con inde-
pendencia del compilador que estemos usando. El serialVersionUID se guarda
como un atributo más de la clase que queremos serializar, con estos modifica-
dores de acceso y nombre:

private static final long serialVersionUID =


7526472295622776147L
CC-BY-SA • PID_00187406 16 Java RMI

Aunque el identificador puede ser cualquier número, para evitar duplicados y serialver
problemas de formato, lo mejor es generarlo de forma automática utilizando la
El JDK incorpora una versión
herramienta llamada serialver, que viene con el JDK. Esta herramienta generará gráfica del serialver. Escribi-
un número único de versión para la clase que se basa en los propios elementos mos en consola serialver -
show, y nos pedirá la localiza-
que contiene la clase. ción de la clase que queremos
versionar. A continuación nos
dará un identificador único pa-
ra la clase.
Finalmente, es importante que mantengamos el modificador private para este
atributo. La razón para ello es que el identificador de versión debe ser exclusivo
de la clase que estamos serializando y no se propague a sus subclases.

Ventajas y problemas de la serialización

En una comunicación RMI, las invocaciones a métodos remotos con


argumentos de instancias de clase (los parámetros pasados durante la
invocación) son copias de las instancias originales (paso de parámetros
por valor), en lugar de referencias directas a éstas (paso de parámetros
por referencia).

En un contexto de una invocación remota (en la misma máquina o en má- Ved también
quinas diferentes), invocador e invocado se encuentran siempre en diferentes
En el subapartado "Paso de pa-
procesos. Si el proceso invocador enviara referencias suyas al proceso invoca- rámetros y valores de retorno"
do, tendría que producirse cada vez un cambio de proceso (atravesando la red trataremos con detalle el paso
por parámetros en el contexto
si los procesos estuvieran separados en máquinas diferentes) para efectuar una de una invocación remota.

nueva invocación. Todo esto reduciría el rendimiento de la aplicación de ma-


nera inaceptable.

Veamos en la figura 3 un ejemplo ficticio de la situación que hemos creado. Ejemplo de la calculadora
En nuestra calculadora remota, si pasamos los operandos enteros como refe-
Fijaos en que, en este ejem-
rencias a objetos de tipo Integer en el servidor, este, al querer recuperar el plo, lo más lógico sería trabajar
int que contiene, deberá invocar el método intValue() del objeto Integer con operandos primitivos de
tipo int, que son serializables
que se encuentra en el cliente. Por lo tanto, será necesario atravesar la red de por defecto. Hemos forzado el
ejemplo a operandos de tipo
nuevo y crear una nueva invocación, esta vez en el cliente. Vemos que en total Integer para mostrar la proble-
mática.
se harán seis invocaciones para efectuar la operación de suma.

Figura 3
CC-BY-SA • PID_00187406 17 Java RMI

Las continuas invocaciones remotas comportarían alargar el tráfico por la red,


lo cual supone aumentar el tiempo de respuesta e incrementa la posibilidad
de fallos en la red. Tampoco sería posible acceder a los atributos públicos del
objeto (si los hubiera), puesto que en las invocaciones vía RMI solo se accede
a métodos de objeto, nunca a atributos.

La figura 4 describe la misma invocación de métodos remotos pero con argu-


mentos que son copias de objetos en vez de referencias al objeto, tal como
realmente sucede en el contexto de RMI. Los operandos enteros se pasan como
objetos de tipo Integer al servidor, y este, al querer recuperar el int que con-
tiene, deberá invocar el método intValue() del objeto Integer, igual que
antes, pero ahora se trata de una invocación local, dentro del mismo proceso
del servidor. Los objetos de los argumentos se encuentran en el servidor mismo
y, por lo tanto, no hay necesidad de salir a la red, lo cual ahorra tráfico de red.

Figura 4

Veamos ahora algunos problemas que también conlleva la serialización.

Pasar copias en lugar de referencias implica problemas potenciales de incon-


sistencia con el objeto original. Es decir, los cambios que efectúe el proceso
remoto sobre la copia no se propagarán al objeto original, ya que, de hacerse
así, representaría transmitir continuamente dichos cambios a través de la red.

Por otro lado, es necesario que las copias sean completas, con todas las copias
de los objetos y tipos primitivos afectados. Esto significa que, al pasar un ob-
jeto, los atributos de dicho objeto pueden referenciar internamente a otros
objetos o tipos primitivos, lo cual obliga recursivamente a incluir una copia
para cada uno de tales objetos y tipos primitivos internos.

Podemos ver el estado de un objeto como un grafo en forma de árbol en el que Tipo objeto
los nodos son los atributos de tipo objeto y las hojas son los atributos de tipo
Por tipo�objeto nos referimos
primitivo. Por lo tanto, una copia completa de un objeto significa copiar todo a instancias de la clase Ob-
el grafo del objeto con todos sus objetos internos, que estarán representados ject de Java o cualquiera de
sus subclases, mientras que los
en forma de subgrafo. De este modo, en el servidor habrá con facilidad muchos tipos�primitivos son los tipos
primitivos de Java (int, char,
long, etc.).
CC-BY-SA • PID_00187406 18 Java RMI

objetos duplicados, ya que cada llamada a un mismo método remoto que tenga
un argumento de tipo objeto significará enviar cada vez una nueva copia del
mismo objeto.

Sin embargo, estos inconvenientes son de poca magnitud en comparación


con los perjuicios que implicaría el paso de parámetros por referencia, como
hemos visto.

Serialización de objetos en RMI

Con todo lo visto hasta ahora, podemos serializar objetos para la comunica-
ción en red con RMI.

En RMI, todos los parámetros de una invocación remota y los valores


devueltos por los objetos remotos deben ser serializables. Por lo tanto,
todo dato que tenga que viajar por la red deberá ser convertido antes
en un stream de bytes.

Hay pocas excepciones a esta regla general, aunque en ciertos casos habrá datos Casos especiales
que no tenga sentido serializar. Para estos casos especiales, disponemos del
Un caso especial es FileIn-
modificador transient para evitar la serialización de aquellos atributos que no putStream; si lo serializáse-
está permitido o que no queremos serializar. mos, al deserializarla más ade-
lante en el servidor, podría re-
ferenciar algún archivo no vá-
lido o sin sentido en el nuevo
Todos los tipos primitivos de Java son serializables por defecto, ya que se pue- contexto. Otro caso es un ob-
jeto de tipo Thread, en cuyo
den convertir a bytes directamente (por ejemplo, el tipo int es una secuencia caso no tiene sentido serializar-
de cuatro bytes). Por este motivo, los tipos primitivos no necesitan ninguna lo al depender su estado de la
plataforma subyacente.
transformación para ser transmitidos por la red. En cuanto a los tipos objeto,
hay que hacerlos serializables explícitamente siguiendo las indicaciones que
exponemos a continuación: Serializable

Encontraréis más información


• La clase que queremos serializar debe implementar (implements) la inter- sobre Serializable en el
paquete java.io de la API de Ja-
faz Serializable. Esta interfaz es de hecho una interfaz vacía, esto es, va.
no contiene ningún método. Su función es simplemente marcar las clases
que queremos hacer serializables para diferenciarlas del resto. Por lo tanto,
RMI solamente serializará los atributos de aquellas clases que implemen-
ten esta interfaz.

• Generar un serialVersionUID para la clase serializable y añadirlo al código


en forma de atributo. Este atributo tendrá modificador de acceso private
para evitar la propagación a sus subclases.

• Los atributos del objeto a serializar tienen que ser o bien de tipo primiti-
vo, o bien de tipo objeto serializable (es decir, que este objeto, a su vez,
sea serializable). Si no es así, conseguiremos una excepción de tipo No-
tSerializableException. Aquellos atributos que no queramos incluir
en la serialización se tienen que modificar con transient. Fijaos en que los
CC-BY-SA • PID_00187406 19 Java RMI

atributos static tampoco se serializarán, ya que su estado tiene que ver con
la clase, y no con la instancia que queremos serializar.

• Asegurarse de que la superclase del objeto que queremos serializar es una


clase serializable. Si no es así, todavía cabe la serialización haciendo persis-
tente el estado de la superclase. Para hacerlo, hay que redefinir los métodos
writeObject() y readObject() para manejar el estado de la superclase
explícitamente desde la subclase.

• Para evitar el problema que hemos identificado de la duplicación de co- equals() y hashcode()
pias de objetos pasados por parámetro en sucesivas invocaciones del mis-
Los métodos equals() y
mo objeto, debemos asegurarnos de que trabajamos siempre con la copia hashcode() forman parte de
correcta. Puede ser necesario comprobar si dos objetos son el mismo me- la clase raíz Object.

diante la redefinición de los métodos equals() y hashcode() del objeto


antes de serializarlo, para que el servidor pueda efectuar dichas compro-
baciones.

Finalmente, hay que mencionar los casos especiales en que el rendimiento de API Reflection
la aplicación distribuida es crítico y el uso de la serialización que ofrece RMI
Consultad la API Reflection en
por defecto puede resultar lento. Esto se debe principalmente al uso que se el paquete java.lang.reflect de
hace de la API Reflection de Java para descubrir información sobre el objeto la API general de Java.

que se está serializando.

Para estos casos, Java proporciona la interfaz Externalizable, que debe ser Externalizable
implementada por las clases en las que queremos un control total en las tareas
Externalizable es otra in-
de serialización y deserialización. Estas tareas se programarán manualmente terfaz del paquete java.io que
ad hoc para los objetos que interesa serializar. Externalizable, de forma pa- hereda de Serializable.

recida a Serializable, marca las clases que no queremos hacer serializables


de forma automática y delega en la clase tal responsabilidad. Con la progra-
mación manual, el mecanismo de serialización conocerá en tiempo de ejecu-
ción toda la información de estos objetos y evitará el uso de la API Reflection.

A pesar de este último punto, la serialización manual exige un importante


esfuerzo, así como disponer de un alto nivel técnico en programación. En la
gran mayoría de los casos, utilizaremos la serialización genérica de RMI, ya
que cubre lo suficiente los casos habituales.

1.3.2. Paso de parámetros y valores de retorno

Acabamos de ver que la interfaz Serializable es una manera de marcar Ved también
una clase para informar de que se trata de un objeto local que serializamos. El
En la asignatura Fundamentos
objetivo es informar al compilador y al entorno de ejecución de Java de que de programación habéis visto el
tendrá que pasar (paso de parámetros) por valor copias de los objetos de esta paso de parámetro por valor y
por referencia.
clase desde la JVM local a la JVM remota en un entorno RMI.
CC-BY-SA • PID_00187406 20 Java RMI

En una invocación remota, también cabe la posibilidad de pasar por parámetro Remote
tipos de objetos remotos. Los tipos de objetos remotos se identifican con la
Encontraréis más información
interfaz Remote. sobre Remote en el paquete
java.rmi de la API de Java.

Remote es una interfaz sin métodos con el propósito de marcar una


clase para informar de que se trata de un objeto remoto que en una
invocación remota de métodos pasaremos por referencia.

(2)
Para entender mejor los tipos objetos remotos2, podemos imaginar un sistema Tened en cuenta que los tipos
objetos remotos son poco habitua-
distribuido complejo en el que hay varios entornos RMI funcionando al mis- les en una invocación remota.
mo tiempo. Durante las invocaciones es posible pasar como parámetro tipos
de objetos remotos pertenecientes a otros entornos RMI diferentes de aquel
en el que nos encontramos. No es posible serializar un objeto de este tipo y
pasar una copia suya a nuestro servidor remoto, ya que estaríamos enviando
la copia a una ubicación diferente de la original, lo cual no tiene sentido. La
única manera posible es pasar una referencia de este objeto, que siempre se
encontrará en su ubicación original y, al ser remoto, esto significa que tiene
capacidad de ser accedido remotamente. En consecuencia, los parámetros de
tipos objetos remotos los pasaremos por referencia.

Durante una invocación a un método de un objeto remoto, los parámetros


que contiene la invocación pueden ser de tres tipos:

1)�Primitivos (int, float, char, etc.), objetos que pasaremos siempre por valor,
puesto que, como hemos indicado, son serializables por defecto (un dato de
tipo int es ya una secuencia de cuatro bytes).

2)�Objetos�serializables (o no remotos), objetos cuya ubicación no es impor-


tante para mantener su estado interno. Se pueden encontrar tanto en la JVM
local como en la JVM remota, sin que esto afecte a su estado. Durante la in-
vocación, pasaremos por valor estos objetos, es decir, pasaremos una copia
de ellos. Junto con los datos de tipo primitivo, son el caso más habitual y fre-
cuente de tipo de datos en el paso de parámetros de una invocación remota.

3)�Objetos�remotos, objetos cuya ubicación sí resulta importante para su esta-


do interno y, en consecuencia, no podemos cambiar su localización enviando
una copia a otra JVM. Estos objetos representan los objetos remotos mismos,
a cuyos métodos hacemos referencia en una invocación remota. A diferencia
de los tipos primitivos y objetos no remotos, los objetos remotos son un caso
poco habitual de tipos de datos en el paso de parámetros.
CC-BY-SA • PID_00187406 21 Java RMI

En la tabla siguiente, a modo de resumen, identificamos los diferentes tipos


de paso por parámetro y situaciones existentes en Java, tanto para invocacio-
nes locales como remotas. Observamos cómo los tipos primitivos siempre son
pasados por valor, mientras que los tipos de objeto remoto son pasados por
referencia.

Invocación Tipo�primitivo Tipo�de�obje- Tipo�de�ob-


to�no�remoto jeto�remoto

Local PV PR PR

Remota PV PV PR

PV = por valor, PR = por referencia.

Han surgido críticas al diferente tratamiento que se da al paso de parámetros al Transparencia de acceso
poner en duda el carácter de transparencia de acceso en RMI. El programador
La transparencia�de�acceso se
debe ser consciente en todo momento de si los parámetros pasados en una puede definir como la capaci-
invocación remota son de tipo objeto remoto o no remoto. En el primer caso, dad de un sistema de ofrecer
siempre las mismas interfaces
tendrá que ser serializado para poder enviar una copia del objeto no remoto al hacer las llamadas a sus ser-
vicios, ya que en caso de no
por la red. En el segundo caso, solo se enviará la referencia del objeto remoto. ofrecer esta transparencia sería
necesario adaptar la llamada
cada vez que cambie la inter-
Finalmente, cuando el objeto servidor devuelve un valor como resultado de faz.

una invocación remota de métodos, este dato siempre se copiará de la JVM


remota en la JVM local, puesto que se tratará o bien de un objeto serializable,
o bien de un tipo primitivo. No es posible devolver tipos de objetos remotos.

1.3.3. Localización

En un sistema distribuido con gran cantidad de objetos almacenados en un


gran número de máquinas, resulta imprescindible disponer de un sistema de
localización. Este sistema debe ser simple y permitir fácilmente a los clientes
encontrar la máquina servidora donde se encuentra el objeto cuyos métodos
quieren invocar y conectar con ella.

Existen diferentes soluciones a este problema, pero pocas que sean simples y Transparencia de
efectivas. Tenemos la opción de que la aplicación cliente use directamente la localización

dirección física del servidor. No obstante, esto rompería el carácter de trans- La transparencia�de�localiza-
parencia de localización, puesto que implicaría actualizar y recompilar cons- ción (también conocida como
transparencia de ubicación) se
tantemente la aplicación a cada cambio de localización del servidor. Otra po- puede definir como la capaci-
dad de un sistema de ofrecer
sibilidad es que el usuario de la aplicación cliente conozca y proporcione la un servicio sin preocuparse de
dónde se ubica físicamente di-
dirección del servidor como entrada de datos, pero esto comporta un trabajo
cho servicio.
incómodo para el usuario.

Una solución mejor para localizar un objeto en un entorno distribuido es crear DNS

un servicio de nombres, como un directorio telefónico, en el que un nombre El conocido sistema de nom-
(dirección lógica) se corresponda con un servidor (dirección física). Este servi- bres de dominio (domain name
system, en inglés) es un ejem-
cio se encontraría en un servidor conocido que, en caso de cambio de locali- plo de servicio de nombres.
zación de los servidores, actualizaría constantemente la parte de la informa-
CC-BY-SA • PID_00187406 22 Java RMI

ción relativa a la dirección física donde se encuentran los objetos remotos. De


este modo, la dirección lógica se correspondería con el auténtico servidor, y
sería transparente al cliente por lo que respecta a los cambios de localización.
También sería lo bastante flexible como para conducir la llamada al servidor
requerido durante la invocación (en tiempo de ejecución), sin ninguna inter-
vención del usuario.

RMI proporciona un servicio de nombres muy simple llamado RMIRe-


gistry, que permite al servidor grabar los objetos remotos para que los
clientes puedan encontrarlos.

Este servicio es válido para aplicaciones poco complejas con pocos objetos Ved también
remotos, pero no resulta adecuado para aplicaciones distribuidas grandes y
Veremos el RMIRegistry con
dinámicas, por la carencia de escalabilidad y de independencia de la máquina más detalle en el subapartado
donde se encuentra. "RMIRegistry" de este módulo.

1.3.4. Activación de objetos remotos

En un modelo de objetos local, los objetos instanciados se encuentran en la


memoria interna de la máquina listos para ser usados. Cuando la aplicación
finaliza, o bien cuando se produce un apagón en la máquina, estos objetos se
destruyen. En este contexto, se trata de un comportamiento adecuado, ya que,
al finalizar la aplicación (por el motivo que sea), la existencia de tales objetos
no tiene sentido.

Sin embargo, este comportamiento no es apropiado cuando pasamos al mo-


delo de objetos distribuidos, por diferentes razones:

• El servidor que contiene el objeto remoto instanciado se puede apagar,


puede averiarse, se pueden agotar sus recursos, etc., con lo cual los obje-
tos que se encontraban en memoria se destruyen y desaparecen. En una
aplicación distribuida, estas situaciones no tendrían que significar el fin
de la aplicación, la cual debe continuar ejecutándose para dar servicio a
sus usuarios.

• El objeto remoto solo es necesario mientras se está ejecutando la aplica-


ción. Sin embargo, una aplicación distribuida puede pasar largos periodos
de tiempo sin actividad (por ejemplo, durante la noche o los festivos), pe-
ro los objetos remotos permanecerán en la memoria del servidor todo el
tiempo. Esto comporta un derroche importante de recursos en el servidor.
CC-BY-SA • PID_00187406 23 Java RMI

Así pues, son necesarios mecanismos adicionales que eviten las situaciones
descritas. Por un lado, es preciso que los objetos remotos consuman recursos
del servidor justo el tiempo necesario que puedan ser invocados. Por otro lado,
en el supuesto de que el objeto se destruya de forma involuntaria, éste debería
ser recuperado por completo y mantener el estado previo.

En el modelo de objetos distribuidos hay dos operaciones, llamadas ac-


tivación y desactivación. La activación instancia objetos remotos que
han sido previamente desactivados o destruidos, mientras que la desac-
tivación efectúa el proceso inverso.

Un objeto remoto inactivo no se encuentra en la memoria del servidor y, por Objetos locales
lo tanto, no consume recursos. El estado que tenía el objeto cuando estaba
Esta diferencia es clara en
activo se guarda en soporte persistente, junto con datos de localización del cuanto a los objetos locales,
código de la clase dentro del servidor, entre otros datos. Todos estos datos se los cuales solo se encuentran
en memoria temporal.
actualizan constantemente mientras el objeto se encuentra activo, y se man-
tienen persistentes en el servidor. Podemos ver que los objetos remotos se en-
cuentran en la memoria temporal, pero al mismo tiempo también se mantiene
una copia persistente del objeto.

El cliente, por su parte, mantiene una referencia a la información persistente Objeto inactivo
sobre los objetos del servidor. Esto le permite activar el objeto remoto en caso
Se dice que si el objeto pasa a
de que este no esté instanciado (inactivo). Esta información permite al cliente inactivo o se ha destruido de
buscar, activar (es decir, instanciar de nuevo) e inicializar un objeto remoto forma involuntaria, no muere,
sino que pasa a stand by.
con los datos de su último estado conocido cuando éste se encuentra inactivo.
Así, se recupera el objeto remoto con el mismo estado que tenía y vuelve a
estar activo y listo para dar servicio a los clientes.

Todos estos mecanismos son, o deberían ser, totalmente transparentes a usua-


rios y programadores. Sin embargo, algunas desventajas suelen romper esta
transparencia. Veamos algunas razones.

Cuando un objeto pasa cierto tiempo sin ser invocado por un cliente, éste
es desactivado. Por eso, hay que prever estrategias y políticas de activación
(umbral de inactividad, qué objetos son más críticos y tienen que estar activos
más tiempo que otros, etc.). Estas estrategias se deben decidir y diseñar a priori
y, en algunos casos, los usuarios tienen que ser conscientes de ellas. Por otro
lado, un objeto remoto inactivo se tiene que activar antes de ser invocado por
los clientes. Esto comporta más tareas y un sobrecoste en comparación con la
invocación de objetos local, que se traduce en una mayor latencia del proceso
de invocación.
CC-BY-SA • PID_00187406 24 Java RMI

Java, en su modelo de objetos distribuidos RMI, dispone de un meca-


nismo de activación de objetos transparente a los usuarios y programa-
dores.

Este mecanismo se basa en dos elementos:

1) un activador�de�objetos, que se encuentra en cada servidor. Está formado


tanto por un gestor de JVM que se encuentra en cada JVM del servidor como
por una tabla de descriptores de activación con información persistente de los
objetos que se ejecutan (objetos activos) en el servidor. Esta tabla se vuelve
persistente en el servidor, junto con información sobre la JVM del servidor
donde se ejecuta el objeto, el nombre de la clase del objeto, la URL donde se
encuentra el código bytecode del objeto y datos de inicialización del objeto (por
ejemplo, el nombre de un archivo con el estado del objeto).

2) un apuntador�inteligente, que se encuentra en el cliente apuntando a la


tabla de descriptores del servidor. Este apuntador contiene tanto un identifi-
cador de activación del objeto remoto (que apunta al activado del servidor
donde se encuentra el objeto) como una referencia viva al objeto remoto, a
partir de la cual es posible saber si el objeto está activo o inactivo.

Cuando se invoca un objeto remoto, la referencia viva del cliente indica en


qué estado se encuentra el objeto. Si este está inactivo, se usa el identificador
de activación para encontrar el activador del objeto. Este activador nos sitúa
en el servidor donde se encuentra el objeto. La tabla de descriptores del activa-
dor proporciona toda la información necesaria para activar el objeto. En caso
de que la JVM correspondiente a este objeto no esté en ejecución, el gestor
de esta JVM lo arrancará. En todos los casos, el activador carga el bytecode de
la URL, instancia el objeto y lo inicializa con sus datos de inicialización que
están guardados. Finalmente, una vez activado el objeto remoto, se devuelve
la referencia remota al activador, que la devuelve al cliente para actualizar la
referencia viva (ahora, el objeto se encuentra activo).

1.3.5. Recolector de basura distribuido

Todos los lenguajes orientados a objetos disponen de mecanismos manuales


o automáticos para eliminar de la memoria aquellos objetos que dejan de ser
necesarios. Este proceso de eliminación es fundamental para una gestión co-
rrecta y eficiente de los recursos de la máquina, al liberar recursos que se pue-
den destinar a otros objetos.
CC-BY-SA • PID_00187406 25 Java RMI

El criterio para decidir qué objetos no son necesarios se basa en com-


probar si se encuentran referenciados por algún cliente. Cuando no hay
ninguna referencia al objeto, se dice que este ya no es necesario y se
elimina.

(3)
Java, como otros lenguajes orientados a objetos, dispone de un mecanismo El recolector de basura se cono-
ce generalmente como garbage co-
automático para recoger estos objetos eliminados llamado recolector�de�basu-
llector.
ra3, que es transparente al programador. Otros lenguajes no disponen de este
sistema automático y delegan este proceso en el programador, que debe deter- Lenguaje C++
minar cuándo y cómo se lleva a cabo. Esto comporta muchos problemas, a
En lenguajes como C++, al
causa de la dificultad de gestionar manualmente los objetos y el impacto que programar un cliente, hay que
tener en cuenta si hay otros
tiene la no eliminación de objetos no necesarios en la eficiencia global.
clientes en el sistema con refe-
rencias al mismo objeto remo-
to.
En los sistemas de eliminación automáticos, todos los objetos son visitados
constantemente, y aquellos que no son referenciados por ningún otro objeto
se eliminan. Este mecanismo se aplica tanto en entornos locales como distri-
buidos. Veamos el funcionamiento en cada caso:

• En entornos�locales, en todo momento hay un conocimiento completo


de las referencias existentes a los objetos. Las referencias a objetos locales
son implementadas como direcciones de memoria que apuntan a la zona
donde se almacenan los objetos. Cuando un objeto en memoria no tiene
ningún apuntador hacia él, la parte de la memoria que ocupa dicho objeto
se libera.

• En entornos�distribuidos, las referencias a los objetos son más comple- Software intermediario
jas. Una referencia debe aportar información, como por ejemplo la locali-
El software o capa intermedia-
zación donde se encuentra el objeto en el sistema, datos sobre el tipo de ria (middleware) se encuentra
objeto e información de seguridad. Toda esta información se almacena en por lo general entre el siste-
ma operativo y las aplicaciones
forma de referencia de tamaño considerable, tanto en el software interme- del sistema, y permite resolver
de forma transparente las ta-
diario del sistema distribuido como en la parte servidora. En este caso, es reas de bajo nivel de las apli-
caciones. También se aplica a
todavía más importante eliminar aquellos objetos del sistema distribuido los sistemas distribuidos como
no referenciados, ya que también resulta necesario eliminar la referencia, una abstracción de todos los
sistemas individuales que los
que ocupa mucho espacio de memoria. conforman.

El recolector�de�basura que utiliza Java para su modelo de objetos dis-


tribuidos RMI se basa en un contador de referencias remotas.

Veamos a continuación el funcionamiento de este mecanismo en RMI.

En una invocación remota, en la que se invocan los métodos de un objeto re-


moto, todas las invocaciones de diferentes clientes a estos métodos se refieren
al mismo objeto remoto, donde los clientes se han registrado previamente.
Para controlar las referencias de los clientes a este objeto, RMI sigue la pista a
CC-BY-SA • PID_00187406 26 Java RMI

todas las referencias dentro de cada JVM. Cada vez que se produce una nueva
referencia a un objeto remoto desde alguna JVM, el contador de referencias
para ese objeto se incrementa. Así, el contador refleja la suma de las referencias
de todas las JVM clientes que acceden al mismo objeto remoto.

Cuando sucede la primera referencia al objeto remoto, se avisa al servidor del


objeto para que lo marque con la etiqueta referenciado. De este modo, el reco-
lector de basura es consciente de ello y no descarta ese objeto. El recolector
de basura va inspeccionando continuamente cada JVM cliente, y cada vez que
encuentra una referencia descartada (es decir, ya no se referencia el objeto re-
moto desde aquella JVM), se decrementa el contador. Cuando el contador lle-
ga a cero, se avisa al servidor para que marque el objeto como no referenciado,
lo cual conduce al recolector de basura a descartarlo.

(4)
Sin embargo, se dan algunos problemas4 con este mecanismo. En caso de que la Hay soluciones a estos proble-
mas, a pesar de que son complejas
red entre clientes y servidor esté separada, la capa de transporte puede no ver a y quedan fuera del alcance de este
los clientes y puede creer que han caído. Esto puede provocar que el recolector módulo.

de basura descarte el objeto remoto de manera prematura, al creer que los


clientes no tienen referencias de él. Como consecuencia, este mecanismo no
puede garantizar la integridad referencial a los objetos remotos, es decir, una
referencia cliente puede no referenciar ningún objeto remoto, y también a la
inversa, un objeto remoto puede ser borrado habiendo todavía referencias a él.

1.3.6. Excepciones

Durante una comunicación en red puede ocurrir gran cantidad de incidencias


(caída de un servidor, problemas de red, fallo de conexión del cliente, etc.). Por
ejemplo, si el servidor cae durante la invocación de un método por parte de
un cliente, RMI lanzará automáticamente una RemoteException en el lado
del cliente.

Ante cualquier incidencia surgida, RMI lanza una excepción de tipo Re-
moteException para avisar al cliente de que algo va mal en el entorno
RMI.

Para asegurar la máxima robustez en el entorno RMI, se introdujo en el com- RemoteException


pilador la obligación de añadir la RemoteException en la cláusula throws
Encontraréis más información
como parte de la firma de cada uno de los métodos del servidor que pueden ser sobre RemoteException en
invocados por el cliente. Esta decisión conlleva la obligación por parte de los el paquete java.rmi de la API
de Java.
clientes de invocar cualquier método remoto dentro de un bloque try-catch
CC-BY-SA • PID_00187406 27 Java RMI

(checked exception). Una consecuencia negativa de esta decisión es que obliga Exception
al programador a generar código en el lado del cliente para tratar excepciones
Todas las excepciones que he-
imprevisibles, lo cual da como resultado código sin demasiada utilidad. redan de Exception se deno-
minan checked exception y se
tienen que tratar obligatoria-
Además, la obligación es doble, ya que solo se permite lanzar objetos exclusi- mente. Java también permite
el uso de unchecked exceptions
vamente de tipo RemoteException (no se permiten las subclases). Esto sig- (heredan de RuntimeExcep-
tion) para evitar tal obliga-
nifica que el tratamiento de otras excepciones que no sean de comunicación ción.
(es decir, las propias de la aplicación) se debe llevar a cabo dentro de la Re-
moteException misma, como una excepción anidada, aprovechando que el
constructor RemoteException(String s, Throwable ex) permite anidar Ved también

otras excepciones. Encontraréis un ejemplo prác-


tico de excepción remota
anidada en el caso de estudio
El proceso de lanzamiento de una excepción en RMI empieza cuando el ser- del apartado "Caso de estudio
RMI" de este módulo.
vidor detecta alguna incidencia durante la invocación. La implementación
de alguno de los métodos lanza una excepción del tipo RemoteException.
El skeleton del servidor serializa el objeto RemoteException lanzado y lo re- Ved también
transmite por la red hasta el stub del cliente, que deserializa este objeto para
Explicaremos el funcionamien-
ser atrapado por un bloque catch. Por lo tanto, RMI aprovecha que median- to de los stubs y skeletons en
detalle en la apartado "Arqui-
te los stubs y skeletons controla la comunicación entre el cliente y el servidor tectura RMI" de este módulo.
para propagar automáticamente una excepción cuando se detecta alguna in-
cidencia.

Finalmente, es importante considerar los métodos locales que se encuentran


en el objeto servidor y que no forman parte de la interfaz remota que da ser-
vicio a los clientes. Estos métodos solo se podrán invocar dentro de la misma
JVM del servidor por medio de una típica invocación local. Por lo tanto, aun-
que formen parte del objeto remoto, los métodos locales no necesitan lanzar
una RemoteException.

1.3.7. Seguridad

El modelo de seguridad de Java para applets (miniaplicaciones) y aplicaciones Bibliografía


se basa en el uso de cargadores de clases, verificadores de código bytecode y complementaria

administradores de seguridad. Sin embargo, este nivel de seguridad es insufi- No es el propósito de la asig-
ciente para aplicaciones distribuidas con RMI. natura explicar el modelo de
seguridad de Java. Aquellos
que estéis interesados, podéis
consultar la obra: J.�Jawors-
Los cargadores�de�clase solo permiten cargar clases que se encuentren en la
ki;�P.�J.�Perrone (2001). Se-
máquina local. Para cargar clases desde ubicaciones remotas es imprescindible guridad en Java. Madrid: Pear-
son Alhambra.
un administrador de seguridad específico que permita cargar las clases remo-
tamente.

El administrador de seguridad que se utiliza en Java para las aplicaciones


distribuidas es la clase RMISecurityManager.
CC-BY-SA • PID_00187406 28 Java RMI

Para disponer de un administrador�de�seguridad para RMI, se crea una instan- RMISecurityManager


cia de RMISecurityManager mediante System.setSecurityManager(new
Encontraréis más información
RMISecurityManager()) al principio de la ejecución del objeto cliente o sobre RMISecurityManager
servidor. Podemos crear subclases de RMISecurityManager y así obtener ad- en el paquete java.rmi de la
API de Java.
ministradores de seguridad a medida (y menos restrictivos) según nuestras ne-
cesidades.

Por otro lado, hemos visto que RMI se basa en la comunicación por sockets, Ved también
mediante los cuales se envían objetos serializados por la red, en principio sin
Revisad el protocolo secure soc-
ningún tipo de cifrado. Aun así, los datos críticos de los objetos serializados y, ket layer (SSL) y la seguridad
en general, toda comunicación que circule por la red, deberían viajar siempre por sockets en la asignatura Ad-
ministración de redes y sistemas
cifrados. Para ello, Java ofrece SSLSocket, una subclase de Socket que per- operativos.

mite utilizar el protocolo SSL para cifrar la información que se intercambian


entre sockets. Java también ofrece la posibilidad de crear sockets específicos para
SSLSocket y
RMI con RMISocketFactory (que son diferentes de la clase Socket). La crea- RMISocketFactory
ción de nuestros propios sockets es imprescindible cuando necesitamos cifrar
Consultad SSLSocket en
o comprimir los datos que se envían al servidor, o bien cuando nuestra apli- el paquete javax.net.ssl y
RMISocketFactory en el pa-
cación RMI requiere diferentes tipos de sockets para distintos objetos remotos. quete java.rmi de la API de Ja-
va.

A partir de la versión 1.2 de Java, se introdujo un entorno de seguridad más


restrictivo para impedir llevar a cabo acciones maliciosas. Se hizo necesario
crear entornos de seguridad a medida mediante permisos personalizados que
se otorgan a aquellas partes del código que deben llevar a cabo acciones crí-
ticas. A causa de estas restricciones de seguridad, es preciso proporcionar per-
misos explícitos al servidor durante una comunicación RMI.

Para este propósito, Java proporciona una serie de tipos de permisos. En una
aplicación típica RMI, el permiso más importante es el de la clase Socket-
Permission, que gestiona permisos de un socket en la red. Un SocketPer-
mission se construye con un nombre de servidor y una serie de acciones per-
mitidas para dicho servidor. Esto se consigue creando un archivo de pólizas
que especifica en forma de permisos cuáles son las políticas de seguridad que
se aplicarán al socket.

Veamos un ejemplo en el que se crea un archivo de pólizas denominado java.policy, en


java.policy
el que especificaremos los permisos dados a los sockets que participan:

grant{ permission java.net.SocketPermission "*:1024-65535", Utilizamos java.policy como


"connect,accept"; nombre del archivo de póli-
zas por convenio, aunque es li-
bre y se puede cambiar por un
permission java.net.SocketPermission "192.192.192.192:80", nombre más adecuado simple-
"connect"; }; mente modificando las propie-
dades del archivo java.security
En este ejemplo proporcionamos permisos para aceptar conexiones. Primero se da per- en el directorio <JAVA HO-
miso para aceptar conexiones y conectarse a cualquier servidor (símbolo "*") entre los ME>/lib/security.
puertos 1024 y 65535 (es decir, todos los puertos que no son del sistema). En el segundo
caso se da permiso de conexión al servidor con dirección 192.192.192.192 por medio
del puerto 80 (servicio HTTP). Crearemos un archivo con el nombre java.policy con este
contenido. Para que el entorno de ejecución lo encuentre, lo guardaremos en el servidor
como valor en la propiedad de seguridad del sistema mediante la instrucción del intér-
prete java -Djava.security.policy=java.policy NombreObjetoServidor.
CC-BY-SA • PID_00187406 29 Java RMI

2. Arquitectura RMI

Como hemos visto, el modelo de objetos distribuido que propone Java permite Objeto remoto y objeto
que los objetos que se ejecutan en una JVM invoquen los métodos de objetos servidor

que se ejecutan en otras JVM. El objeto que hace la invocación se denomina Utilizamos los términos obje-
objeto�cliente, mientras que el objeto remoto que recibe la invocación de sus to remoto y objeto servidor co-
mo sinónimos, a pesar de que
métodos se denomina objeto�servidor. El objeto remoto tiene que estar regis- generalmente se entiende que
los objetos servidores adminis-
trado en un servidor de nombres. tran los objetos remotos.

En este modelo, el objeto cliente nunca hace referencia directamente a un


Ved también
objeto servidor, sino a una interfaz remota que es implementada por este. Di-
cha interfaz actúa como servidor intermediario, también llamado fragmento Hablaremos ampliamente
de los stubs y skeletons en el
adaptador stub, que implementa los métodos remotos. Éste se comunica por subapartado "Stubs y skeletons"
la red mediante sockets con su homólogo del servidor, denominado skeleton, de este módulo.

responsable de la comunicación con los objetos remotos del servidor. Tanto


los stubs como los skeletons son generados automáticamente como clases du-
rante la compilación, y son los auténticos responsables de la comunicación
por la red.

Figura 5

En la figura 5 se puede observar una vista general de la arquitectura básica


de una comunicación cliente y servidor en el entorno RMI para llevar a cabo
una invocación de un método remoto con llamada y retorno (flechas bidirec-
cionales). Las líneas punteadas representan la comunicación virtual o lógica,
mientras que las líneas continuas representan la comunicación real (se mues-
tra la comunicación del nivel de transporte). Vemos cómo tanto los stubs como
los skeletons se comunican por medio de sockets, lo que genera el stub durante
la llamada al método remoto. RMIRegistry y el objeto servidor se comunican
CC-BY-SA • PID_00187406 30 Java RMI

durante el registro del objeto, así como durante la consulta del stub para en-
contrar el objeto. El protocolo de red TCP/IP representa el nivel de transporte
de la red entre la JVM local y la remota.

En los apartados siguientes, describiremos en detalle cada uno de estos com- Ved también
ponentes que conforman la arquitectura RMI.
La arquitectura mostrada en la
figura 5 nos servirá de guía pa-
2.1. El servidor ra el resto del módulo.

La parte�servidora de una aplicación RMI contiene fundamentalmente


los objetos remotos que los clientes buscan para invocar sus métodos y
toda la infraestructura necesaria para recibir las invocaciones y devolver
los valores de los métodos ejecutados.

Como se ha dicho anteriormente, los clientes no referencian directamente los Más información
objetos remotos, sino una interfaz remota que es implementada por el objeto
Encontraréis más información
servidor. En el caso más simple, en el servidor debe haber una clase interface de las clases que se mencionan
que extienda la interfaz Remote con la firma de todos los métodos remotos (es en este apartado en el paquete
java.rmi de la API de Java.
decir, aquellos que lancen RemoteException en su cláusula throws). Estos
métodos son los que el cliente puede invocar.

La interfaz Remote se utiliza de manera parecida a Serializable, de modo Ved también


que los objetos que implementan esta interfaz quedan identificados como ob-
En el subapartado
jetos remotos y el entorno les proporciona comportamiento remoto. Esta in- "Serialización" (punto "Seriali-
terfaz tiene que ser implementada por una clase servidora, y juntas forman el zación de objetos RMI") de es-
te módulo hemos explicado la
objeto remoto del servidor en tiempo de ejecución. interfaz Serializable.

Esta clase servidora se basa principalmente en el paquete java.rmi.server


y tiene, en el caso más básico, las siguientes tareas principales:

• Implementar�la�interfaz�remota, que implica implementar todos y cada


uno de sus métodos remotos. En caso de que necesitemos incluir métodos
locales para que el servidor mismo los utilice, los añadiremos sin la cláu-
sula RemoteException.

• Extender� UnicastRemoteObject para que el servidor contenga obje- UnicastRemoteObject


tos de esta clase accesibles por medio de conexiones TCP. UnicastRemo-
UnicastRemoteObject es
teObject hereda de RemoteServer que, a su vez, hereda de RemoteOb- una clase que se encarga de la
ject. Esta última es la que proporciona una implementación remota a mayor parte de las tareas aso-
ciadas con la creación de un
la clase Object por medio de redefinir de forma apropiada los métodos servidor RMI (como crear un
socket en el servidor y escuchar
equals(), toString() y hashCode(). Con esto se consigue propor- invocaciones de método del
cliente).
cionar un comportamiento remoto predeterminado a los objetos remotos
del servidor, que extienden UnicastRemoteObject.
CC-BY-SA • PID_00187406 31 Java RMI

• Crear�y�registrar�el�objeto�remoto. El registro del objeto remoto se hará en Ved también


el servicio de nombres del sistema con un nombre identificador para este
Detallaremos el funciona-
objeto que lo ligue (bind) al objeto remoto en cuestión. Se usará el méto- miento de RMIRegistry en el
do Naming.rebind("rmi://dir_servidor:puerto/NombreIdObje- subapartado "RMIRegistry" de
este módulo.
to" obj), donde dir_servidor es la IP o el nombre DNS del servidor,
puerto será un número de puerto que no use el sistema, NombreIdObje-
to es el identificador elegido para encontrar el objeto remoto, y obj es el
objeto remoto registrado. Para la mayoría de las aplicaciones se utilizará
RMIRegistry, que es el servicio de nombres que ofrece el JDK con la herra-
mienta rmiregistry.

Crear y registrar el objeto remoto

En el caso más simple, se graba el objeto servidor mismo, que ya contiene el objeto re-
moto.

Si cliente y servidor se encuentran en la misma máquina, la IP es 127.0.0.1. y el nombre


DNS es localhost. Los puertos que no usa el sistema (rango usuario) son: 1024-65535.
rmiregistry escucha por defecto por el puerto 1099.

• Arrancar�el�servidor mediante la instanciación del objeto servidor mismo


dentro de su método main(). Al arrancar el servidor, se llevan a cabo todas
las acciones que acabamos de comentar.

En la parte servidora, es imprescindible disponer de un esqueleto del objeto Ved también


servidor (llamado skeleton) para poder recibir y tratar adecuadamente las in-
Ampliaremos la información
vocaciones de los clientes. El paquete java.rmi.server también incluye la sobre stubs y skeletons en el
interfaz Skeleton, que los skeletons implementan durante su generación. Se subapartado "Stubs y skeletons"
de este módulo.
trata de un elemento de más bajo nivel que el objeto remoto y que, junto con
su homólogo stub en el cliente, forman la conexión entre cliente y servidor
por donde circula toda la información intercambiada.

2.2. El cliente

El cliente es el que invoca los métodos del objeto servidor. En este caso, para
invocar un método remoto, el cliente debe llevar a cabo las tareas siguientes:

• Localizar�el�objeto�remoto. De manera simétrica al proceso seguido pa- Más información


ra grabar el objeto en el servidor, para localizar el objeto grabado en
Encontraréis más información
el servicio de nombres se utiliza el método Naming.lookup("rmi:// de las clases que se mencionan
dir_servidor:puerto/NombreIdObjeto"), en el que dir_servidor en este apartado en el paquete
java.rmi de la API de Java.
y puerto tienen el mismo significado que hemos visto antes en el servi-
dor. NombreIdObjeto tiene que ser el mismo nombre utilizado por el ser-
vidor durante el registro para ligarlo (bind) con el objeto remoto. El méto- Ved también

do devuelve un objeto de tipo Object, al cual es necesario hacer un cast En la asignatura Diseño y pro-
para convertirlo en el tipo del objeto remoto. gramación orientada al objeto
habéis visto el mecanismo de
cast de Java.
CC-BY-SA • PID_00187406 32 Java RMI

• Las invocaciones� a� los� métodos� remotos se tienen que llevar Ved también
a cabo desde dentro de un bloque try/catch para capturar co-
En el subapartado "Excepcio-
mo mínimo la RemoteException lanzada desde el servidor. En ca- nes" de este módulo hemos
so de problemas en el proceso de localización del objeto remo- hablado de las excepciones de
Java en las invocaciones remo-
to, varias excepciones del tipo java.net.MalformedURLException y tas.

java.rmi.NotBoundException pueden ser lanzadas de manera automá-


tica por la JVM, y serán capturadas en este mismo bloque.

De forma simétrica al esqueleto del servidor (skeleton), en el lado del cliente es


imprescindible disponer de un fragmento adaptador del objeto servidor (stub)
para poder tratar y enviar adecuadamente las invocaciones de los clientes y
recibir los valores de vuelta del servidor. Es una representación local de un
objeto remoto que implementa todos los métodos invocables remotamente
de dicho objeto remoto.

El paquete java.rmi.server incluye la clase RemoteStub, que extiende Re-


moteObject y proporciona una implementación abstracta de los fragmentos
adaptadores de los objetos servidores del lado del cliente. A continuación se
explican con detalle tanto los stubs como los skeletons.

2.3. Stubs y skeletons

El mecanismo de invocación remota RMI se basa en dos tipos de objetos ge-


nerados por el compilador de RMI: los stubs y los skeletons.

Un stub o fragmento�adaptador se encuentra en el lado del cliente y es


una representación local de un objeto remoto del servidor que referen-
cia todos los métodos remotos de dicho objeto. De forma equivalente,
un skeleton representa un esqueleto del objeto remoto y se encuentra
en el lado del servidor.

Es decir, un stub tiene implementados los mismos métodos que el objeto re-
moto, pero el código de esta implementación consiste solo en una referencia
completa con todo lo necesario para comunicarse con los métodos del objeto
remoto (el código real de implementación de los métodos se encuentra en el
objeto remoto).

La función principal de un stub es crear y mantener una conexión de sockets stub


abierta con el skeleton del servidor y responsabilizarse de la tarea de serializar
Se dice también que el stub re-
y deserializar los datos en el lado del cliente. De forma parecida, el skeleton presenta un objeto del servi-
se responsabiliza del mantenimiento de la conexión de sockets con el stub, así dor dentro de la JVM del clien-
te que implementa los méto-
como de serializar y deserializar los datos en el lado del servidor. dos del objeto servidor.
CC-BY-SA • PID_00187406 33 Java RMI

Tanto el stub como el skeleton representan clases que se generan automática- rmic
mente durante la compilación (por lo general, con el compilador de RMI rmic)
rmic es una aplicación que vie-
a partir de las clases que implementan la interfaz remota. Durante la invoca- ne de serie con el JDK, y que
ción, se trabaja con instancias de stubs y skeletons, con lo que se mantiene el permite generar stubs y skele-
tons. Una vez generados, es
modelo de objetos que propone Java. Aun así, en las últimas versiones de Java preciso colocar el stub en el
lado del cliente y el skeleton
los stubs y skeletons ya no son necesarios, como se explica en el subapartado en el lado del servidor (o en el
mismo directorio si hace fun-
siguiente. ciones de cliente y servidor en
aplicaciones simples).

2.3.1. Evolución de los stubs y skeletons en Java

API Reflection
A pesar de lo que hemos explicado hasta ahora, la generación de los skeletons
solo fue necesaria antes de llegar a la versión 1.2 de Java. A partir de esta ver- Consultad la API Reflection en
el paquete java.lang.reflect de
sión se suprimió la necesidad de generar los skeletons sustituyéndolos por la la API general de Java.
capacidad de reflexión que ofrece la API Reflection de Java. Este mecanismo per-
mite conocer e invocar los métodos del objeto remoto en tiempo de ejecución.
Aun así, los skeletons son necesarios para la compatibilidad hacia atrás con
aplicaciones escritas en versiones antiguas de Java.

A partir de la versión 1.5 de Java, también se suprimió la necesidad de generar


los stubs. Sin embargo, en este caso, no significa que no existan ni que se hayan
sustituido por otro mecanismo, sino que el entorno de ejecución es capaz de
generarlos de forma automática mediante técnicas de compilación dinámica.
En definitiva, en las últimas versiones de Java ya no es necesario utilizar rmic
para obtener los stubs ni los skeletons.

Aun así, tanto los stubs como los skeletons resultan necesarios para mantener
la compatibilidad hacia atrás, además de que utilizarlos es conceptualmente
más didáctico. Por estas razones, nosotros los utilizaremos en este módulo, así
como rmic para generarlos.

2.3.2. Proceso de invocación cliente/servidor

Los stubs y skeletons son la clave del modelo que ofrece RMI, puesto que aho-
rran al programador todo el trabajo que hacen ellos de manera automática
(crear y mantener los sockets, serializar y deserializar la información transmiti-
da, controlar la conexión, etc.). Al cumplir todas estas funciones transparentes
al programador, se consiguen muchos de los objetivos que hemos menciona-
do de RMI.

(5)
Conozcamos5 este proceso automático que sigue una invocación típica clien- Para seguir mejor este proceso,
observad la figura 5.
te-servidor, a bajo nivel. Una vez que el servicio RMIRegistry está activo, los
objetos remotos están correctamente registrados y el cliente ha encontrado el
objeto remoto, el proceso básico para invocar un método de este objeto es el
siguiente:
CC-BY-SA • PID_00187406 34 Java RMI

1) El cliente obtiene una instancia del stub, que, como hemos dicho, representa
todos los métodos remotos.

2) El cliente llama a un método del stub en una invocación del mismo tipo
que se haría en una invocación local. Es decir, el stub es una representación
del objeto remoto para el cliente, que se encuentra en su misma JVM, lo cual
permite acceder a estos métodos de manera local.

3) El stub en el lado del cliente crea internamente un socket con el skeleton Ved también
en el lado del servidor, lo cual permite tenerlos a los dos conectados. Como
Repasad lo que hemos estudia-
sabemos, el socket enviará los datos a través de la red en forma de stream. do sobre streams y la serializa-
ción en el subapartado "Seriali-
zación" de este módulo.
4) El stub serializa toda la información asociada con la invocación (nombre
del método invocado, tipos de parámetros, etc.) y envía esta información por
medio del socket al skeleton.

5) El skeleton deserializa los datos que le llegan y hace la llamada al método


real del servidor. De manera análoga a la relación entre el stub y el cliente,
la invocación entre el skeleton y el servidor es también local, puesto que se
encuentran en la misma JVM.

6) En caso de que el método invocado devuelva un valor, éste seguirá el camino


contrario de la llamada al método: el skeleton recibirá dicho valor, lo serializará
y lo enviará por medio del socket al stub, que a su vez lo deserializará y lo
devolverá al objeto cliente que ha hecho la invocación.

2.4. RMIRegistry

Como hemos introducido en apartados anteriores, el registro de los objetos se rmiregistry


hace en RMI por medio del servicio RMIRegistry. Puede haber otros servicios
La instrucción rmiregistry
de registro que proporcione nuestro sistema. Sin embargo, al venir de serie con ejecuta la herramienta rmire-
el JDK, es el servicio más ampliamente utilizado. gistry del JDK, que proporciona
el servicio de nombres RMIRe-
gistry.

El paquete java.rmi.registry contiene todas las interfaces y clases que se


usan para registrar objetos remotos mediante un nombre y acceder a ellos.
Registry y Naming
Cuando se ejecuta la herramienta rmiregistry, se abre la posibilidad de registrar
los objetos remotos identificándolos con un nombre. Este paquete contiene Podéis encontrar la interfaz
Registry y la clase Naming
la interfaz Registry, que define métodos bind(), rebind(), list() y en el paquete java.rmi de la
API de Java.
lookup(), entre otros, que utiliza la clase Naming para asociar estos nombres
de objeto y la dirección URL donde se encuentran, así como para localizarlos.

RMIRegistry funciona como un directorio de objetos: un nombre lógico o iden-


tificador del objeto se corresponde con el stub del objeto. Durante el registro,
el servidor tiene que enviar el stub del objeto a RMIRegistry para registrarlo,
con lo que se crea un vínculo (bind()) entre un nombre identificador del
objeto y el stub de dicho objeto. De este modo, las aplicaciones clientes solo
tienen que conocer la localización de RMIRegistry y el nombre identificador
CC-BY-SA • PID_00187406 35 Java RMI

del objeto para encontrarlo (lookup()) en tiempo de ejecución. RMIRegistry


proporciona una lista (list()) con todos los nombres identificadores que
hay en el registro.

Por motivos de seguridad, RMIRegistry se sitúa en la misma máquina del ser-


vidor junto con los objetos remotos. En caso de encontrarse en máquinas di-
ferentes, sería muy fácil reemplazar los servidores reales por otros maliciosos
de forma que RMIRegistry apuntara a estos últimos. Esto representa que RMI-
Registry debe funcionar como un servidor virtual dentro del servidor mismo.

Dado que tanto RMIRegistry como los objetos remotos tienen que encontrarse URL
en la misma máquina, el stub conoce la máquina donde se encuentra el objeto
La URL que usarán los clientes
(que es la misma máquina donde se encuentra el servidor). De este modo, si para llamar a RMIRegistry ten-
trasladamos la parte servidora a otra máquina, el stub solo tendrá que llamar a drá normalmente este forma-
to:
la URL de RMIRegistry de aquella misma máquina servidora, cuya dirección ya rmi://
direccion_servidor:
conoce. Además, puesto que el objeto remoto ha quedado ligado al stub du- puerto/identificador
rante el registro, aunque el objeto cambie de localización dentro de la máqui-
na servidora, será transparente para el stub, que lo encontrará de todas formas.

Por otro lado, si el servicio RMIRegistry cambia de máquina (a otra diferente


del servidor) o solo escucha en otro puerto de la misma máquina, hay que
actualizar la URL, lo cual significa recompilar los clientes. Por lo tanto, RMI
puede llegar a ser transparente por lo que respecta a la localización de objetos
dentro de la misma máquina, pero no a la ubicación de la máquina, lo cual es
mucho más importante y es especialmente grave en aplicaciones distribuidas
altamente dinámicas.

A pesar de los problemas que comporta, RMIRegistry es suficiente para aplica-


ciones sin grandes pretensiones, como las que trabajaremos en esta asignatura,
y resulta muy práctico utilizarlo, al formar parte de los recursos que proporcio-
na Java de serie y así dar soporte completo a su modelo de objetos distribuido.

Es posible reemplazar RMIRegistry por otro servicio de nombres. En el mercado


se pueden encontrar otros servicios de nombres más flexibles y potentes que
RMIRegistry. También podemos desarrollar nuestro propio servicio de nombres
ajustado a nuestras necesidades, aunque, como siempre, esto supondrá un ma-
yor esfuerzo durante la programación.
CC-BY-SA • PID_00187406 36 Java RMI

3. Caso de estudio RMI

En este apartado vamos a poner en práctica los conceptos aprendidos hasta


ahora. Vamos a construir una aplicación distribuida, aunque muy básica, con
RMI. Primero veremos toda la mecánica que hay que seguir para un desarrollo
y ejecución paso a paso manuales. A continuación, aprenderemos a automati-
zar los pasos para poder ejecutar la aplicación de forma repetida en un entorno
de pruebas. Finalmente, daremos las pautas para trasladar la ejecución a un
entorno distribuido cliente-servidor entre ordenadores diferentes.

Con este caso de estudio comprobaremos los efectos de la transparencia que


ofrece RMI, que consigue que no haya diferencias significativas entre invocar
un método de un objeto remoto que se encuentra en otra JVM en el mismo or-
denador e invocar un método de un objeto remoto en un ordenador separado.

Por otro lado, también podremos comprobar la consecución de otros objetivos


de RMI, como su simplicidad y escalabilidad, que permiten mantener la mis-
ma arquitectura con independencia del tamaño de la aplicación distribuida.
Veremos que las tareas a llevar a cabo con RMI son las mismas en una aplica-
ción compleja que en nuestro caso de estudio básico.

3.1. Un ejemplo de RMI básico: una calculadora remota

Para este caso de estudio, implementaremos una calculadora primitiva con una
funcionalidad elemental que nos permitirá sumar, restar, multiplicar y dividir
dos operandos enteros. Su única particularidad será que la podremos usar de
manera remota en un entorno cliente-servidor. El ordenador que contendrá el
objeto que hará los cálculos (la lógica de las operaciones) cumplirá funciones
de servidor, mientras que el ordenador con el objeto que representa el teclado
y el visor de la calculadora (escribe los operandos y operador y muestra el
resultado) tendrá las funciones de cliente.

Para llevar a cabo un cálculo, se crearán dos procesos: proceso servidor y pro-
ceso cliente. El proceso cliente enviará los operandos y el operador al proceso
servidor, que efectuará el cálculo y devolverá el resultado al proceso cliente,
que a su vez lo mostrará al usuario. Así pues, el cliente no dispondrá de la
lógica de cálculo de la calculadora, pero podrá igualmente efectuar cálculos.
CC-BY-SA • PID_00187406 37 Java RMI

Los dos procesos serán independientes el uno del otro y estarán en máquinas Procesos cliente y servidor
diferentes en las que haya instalada una JVM y estén conectadas mediante una
Los dos procesos también pue-
red TCP/IP (por lo general, Internet). Solo es necesario que el cliente conozca den correr como dos procesos
la dirección IP (o nombre DNS) y el puerto del ordenador servidor. independientes dentro de la
misma máquina. En este caso,
la dirección IP (local) y el puer-
to tanto del servidor como del
En los subapartados siguientes explicaremos el desarrollo completo del servi- cliente son los mismos.
dor y el cliente en una aplicación típica RMI. Procederemos primero a desa-
rrollar el servidor y después el cliente, para disponer del objeto remoto antes
Nota
de que pueda ser invocado.
Durante el desarrollo del ejem-
plo no repetiremos las especifi-
Mostraremos todo el código fuente de nuestra calculadora, que hará las fun- caciones de la arquitectura bá-
sica RMI vista en el apartado
ciones de guía durante todo el apartado.
anterior, aunque insistiremos
en aquellos puntos clave que
ayuden a la comprensión del
3.1.1. Desarrollar el objeto remoto código.

Definimos la parte servidora, que consistirá en tres partes:

1) La interfaz� remota, que definirá los métodos que pueden ser invocados
por el cliente.

2) La implementación�de�esta�interfaz, que implementará cada uno de los


métodos de la interfaz. Además, puede implementar otros métodos no remo-
tos en caso de ser necesario para trabajar de manera local en el lado del ser-
vidor.

3) El servidor propiamente dicho, que llevará a cabo las tareas de administra-


ción y ejecución del servidor: instalar un controlador de seguridad, instanciar
el objeto remoto a partir de la interfaz y su implementación, situar el objeto
remoto en el registro para hacerlo visible a los clientes y arrancar la parte ser-
vidora, lo que permitiría a los clientes invocar métodos remotos.

Vamos a describir cada una de las tres partes junto con el código generado de
nuestra calculadora.

La interfaz remota: definición del objeto remoto

Como hemos repetido en varias ocasiones, no es posible acceder a los objetos


remotos directamente, solo son accesibles por medio de su interfaz. Esta inter-
faz deberá extender la clase Remote para identificar el objeto como remoto y
contener la firma de todos los métodos remotos, los cuales por requisito tienen
que lanzar la excepción RemoteException en su cláusula throws.
CC-BY-SA • PID_00187406 38 Java RMI

Tendremos tantas interfaces como objetos remotos haya en nuestra aplicación. Calculadora
Cada interfaz se tratará en un archivo de código aparte. En nuestro ejemplo
Veamos cómo se declaran las
solo hay un objeto remoto y, por lo tanto, una única interfaz. Esta interfaz está firmas de los cuatro métodos
contenida en el archivo Calculadora.java, con el código siguiente: remotos que se corresponden
con las cuatro operaciones que
efectúa la calculadora remota.
importe java.rmi.*;
public interface Calculadora extends Remote {
public int sumar(int a, int b) throws RemoteException;
public int restar(int a, int b) throws RemoteException;
public int multiplicar(int a, int b) throws RemoteException;
public float dividir(int a, int b) throws RemoteException;
}

La implementación de la interfaz: implementar el objeto remoto

Para la implementación de la interfaz, crearemos la clase CalculadoraImpl. Impl


Como ya hemos visto, esta clase tiene que extender la clase UnicastRemo-
El sufijo Impl se usa como
teObject y capacitarla para manejar la mayor parte de las tareas asociadas con convenio para indicar que se
la creación de un servidor RMI. También, como es lógico, debe implementar trata de la clase que imple-
menta un objeto remoto, aun-
la interfaz remota, en este caso la interfaz Calculadora. que el nombre de dicha clase
es totalmente libre.

En el código siguiente (archivo CalculadoraImpl.java) podemos ver la imple-


mentación de los métodos que definen la aritmética de las operaciones de la
calculadora:

import java.rmi.*;
public class CalculadoraImpl extends
java.rmi.server.UnicastRemoteObject implements Calculadora {
//Contructor
public CalculadoraImpl() throws RemoteException {
super();
}
public int sumar(int a, int b) throws RemoteException {
return a + b;
}
public int restar(int a, int b) throws RemoteException {
return a - b;
}
public int multiplicar(int a, int b) throws RemoteException {
return a * b;
}
public float dividir(int a, int b) throws RemoteException {

if(b==0) { //división por cero


//lanza excepción anidada dentro de RemoteException
throw new RemoteException("",new ExcepcionDivisionPorCero(a));
}
return (float)a/(float)b;
}
}

ArithmeticException

Java dispone de la clase ArithmeticException para tratar condiciones aritméticas ex-


cepcionales, como la división por cero. Aun así, proponemos una excepción propia (Ex-
cepcionDivisionPorCero) para tratar esta condición aritmética particular como ejem-
plo de excepción remota anidada.
CC-BY-SA • PID_00187406 39 Java RMI

La clase servidora

Para acabar con la parte servidora, crearemos el servidor propiamente dicho,


que llevará a cabo las tareas siguientes:

• Establecer�un�controlador�de�seguridad. En este caso, se trata de un ob-


jeto de la clase RMISecurityManager. Este controlador satisface el requi-
sito que impone RMI para descargar los objetos al cliente, que son pasa-
dos como parámetros durante la invocación de métodos al servidor. En
el apartado siguiente se da más información sobre los controladores de
seguridad.

• Crear�el�objeto�remoto�Calculadora a partir de la clase que la imple-


menta: Calculadora c = new CalculadoraImpl();.

• Se�asigna�un�nombre�identificador�al�objeto�remoto, que servirá para Calculadora


que los clientes encuentren dicho objeto remoto en el momento de la
En nuestro ejemplo, trabaja-
invocación. El servidor asigna el identificador ServicioCalculadora al remos con la misma máqui-
objeto remoto Calculadora y lo registra en el servicio de nombres del na para el cliente y el servidor.
La URL será rmi://local-
sistema, que en nuestro caso es RMIRegistry. De este modo, los clientes host:1099 (localhost indica
la dirección local del sistema,
podrán localizar en el registro el objeto remoto Calculadora a partir de que se corresponde con la IP
127.0.0.1), y el puerto 1099
este identificador. Para efectuar el registro, es necesario especificar la URL es el asignado por convenio a
de RMIRegistry. RMIRegistry.

El código del archivo CalculadoraServidor.java queda de la manera siguiente:

public class CalculadoraServidor {


public CalculadoraServidor() {
try {
if(System.getSecurityManager() == null)
System.setSecurityManager(new java.rmi.RMISecurityManager());
Calculadora c = new CalculadoraImpl();
java.rmi.Naming.rebind("rmi://localhost:1099/ServicioCalculadora", c);
}
catch (Exception i) {
System.out.println("Problema encontrado: " + i); }
}
public static void main(String args[]) {
new CalculadoraServidor();
}
}

3.1.2. Creación del archivo de políticas

Como acabamos de mencionar, durante el desarrollo del servidor, es necesario


imponer un control de seguridad antes de que el servidor pueda cargar alguna
clase externa. Desde Java 1.2 es necesario configurar la política de seguridad
CC-BY-SA • PID_00187406 40 Java RMI

explícitamente como una propiedad del sistema, o de lo contrario recibiremos


excepciones del tipo SecurityException. A esta propiedad se le asigna co-
mo valor un archivo de pólizas que contiene información sobre todos los per-
misos explícitos otorgados a la clase que se quiere ejecutar (en nuestro caso,
CalculadoraServidor).

En el apartado "Arquitectura RMI" hemos visto que este archivo de políticas se


denomina java.policy por convenio, y representa la política de seguridad que
aplicamos a un sistema que se pretende proteger. En nuestro caso, este archivo
debe contener la información a escala de permisos para autorizar al servidor
la carga de clases externas. Para nuestros fines, tiene que proporcionar, como
mínimo, los permisos de conexión, aceptación, escucha y resolución de todos los
puertos del sistema sin privilegios (por encima del 1024) que pueda utilizar la
aplicación, para que cliente y servidor se comuniquen. El contenido mínimo
del archivo java.policy tendría que ser:

grant {
permission java.net.SocketPermission
"*:1024-65535","connect,accept,listen,resolve";
};

Una vez creado este archivo con la información pertinente, se tiene que en-
contrar en el mismo directorio que la clase servidora, para que el entorno
RMI lo encuentre en el momento de ejecutar la clase CalculadoraServi-
dor. Esto se consigue configurando una propiedad del sistema a partir del par
nombre_propiedad=valor donde nombre_propiedad = java.security.policy
y valor= java.policy.

3.1.3. Desarrollo del cliente

Una vez acabada la parte del servidor, desarrollaremos el cliente, que solo lle-
vará a cabo tres tareas esenciales:

1)� Localizar� el� objeto� remoto. El cliente buscará el objeto remoto a partir Puerto predeterminado
del método Naming.lookup() y el identificador del objeto. La situación es
Aunque el puerto predetermi-
simétrica a la que acabamos de ver al desarrollar la clase servidora. Primero nado por el que RMIRegistry
se tiene que contactar con el servicio de nombres (en nuestro caso es RMIRe- escucha es el 1099, se puede
cambiar por otro siempre que
gistry) en la misma dirección en la que hemos registrado el objeto durante el lo modifiquemos tanto en el
cliente como en el servidor.
desarrollo del servidor. Pasando el identificador por parámetro de lookup(),
el servicio devolverá el objeto remoto de tipo Object que tendremos que mo-
delar (cast) al tipo que nos interesa (Calculadora). Dado que la ejecución en
principio se da en un contexto local, la dirección de RMIRegistry será rmi://
localhost:1099, igual que en el servidor.
CC-BY-SA • PID_00187406 41 Java RMI

2)�Invocar�un�método�del�objeto�remoto. Una vez localizado y recuperado


el objeto, tendremos capacidad para invocar sus métodos, algo que haremos
dentro de la cláusula try-catch, para atrapar objetos RemoteException que
nos informen de los problemas surgidos en el servidor o la red durante la in-
vocación.

3)�Recibir�el�resultado�de�la�ejecución, ya sea con el resultado esperado, ya sea


en forma de notificación sobre una excepción ocurrida durante la ejecución.

Finalmente, el cliente está protegido de diferentes tipos de posibles excepcio-


nes que pueden ocurrir:

• MalformedURLException: URL de RMIRegistry no correcta.


• RemoteException: excepción del lado del servidor.
• NotBoundException: objeto remoto no existente en el registro.
• ArithmeticException: condición aritmética excepcional.
• Exception: cualquier otra causa.

El código cliente que se muestra a continuación espera tres datos por línea de
orden de la consola, por este orden:

1) operando 1: un valor de tipo int


2) operador: un carácter con uno de los cuatro tipos de operador posibles, que
son +, -, x, /
3) operando 2: un valor de tipo int

A partir de estos datos, el cliente seleccionará el método remoto para invocar


según el tipo de operador indicado, pasará los dos operandos como parámetros
y recibirá el resultado de la operación, que mostrará por la salida estándar del
propio cliente.

A continuación se muestra el código del cliente, formado por dos clases. Calculadora
Primero, mostramos el código del archivo ExcepcionDivisionPorCero.java, que
Fijaos en el hecho de que,
es la clase que permite recoger y tratar en el lado del cliente la excep- en el código, el mensaje
ción anidada que está dentro de la excepción lanzada de forma remota. A anidado se encuentra al fi-
nal del mensaje remoto lan-
continuación mostramos el código del cliente propiamente dicho (archivo zado por RemoteExcep-
tion. La parte precedente
CalculadoraCliente.java), que permite trabajar con la calculadora: del mensaje no nos interesa.
Sin embargo, podéis impri-
mirlo completo sustituyendo
//Creamos la clase de excepción anidada para recogerla en el cliente substring(indiceAnidado
public class ExcepcionDivisionPorCero extends Exception { +1) por substring(0) y ver
int dividendo = 0; el mensaje remoto entero.
public ExcepcionDivisionPorCero (int pDividend) {
dividendo = pDividendo;
}
public String getMessage() {
return "%" + dividendo + "/0";//% es un marcador
}
}
//Implementación de la clase cliente
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
CC-BY-SA • PID_00187406 42 Java RMI

import java.rmi.NotBoundException;
public class CalculadoraCliente {
public static void main(String[] args) {
int op1, op2;
String operador;
if (args.length != 3) {
System.out.println(
"Error. Uso: CalculadoraCliente operando1 operador operando2");
return;
}
try {
op1 = Integer.parseInt(args[0]);
op2 = Integer.parseInt(args[2]);
operador = args[1];
Calculadora c = (Calculadora)Naming.lookup(
"rmi://localhost:1099/ServicioCalculadora");
if (operador.equals("+"))
System.out.println(c.sumar(op1, op2));
else if (operador.equals("-"))
System.out.println(c.restar(op1, op2));
else if (operador.equals("x"))
System.out.println(c.multiplicar(op1, op2));
else if (operador.equals("/"))
System.out.println(c.dividir(op1, op2));
else
System.out.println("Error. Uso: CalculadoraCliente operando1
operador operando2");
}
catch (MalformedURLException murle) {
System.out.println("MalformedURLException" + murle);
}
catch (RemoteException re) {
//comprobamos si lleva la marca de excepción anidada
int indiceAnidado = re.getMessage().indexOf('%');
if(indiceAnidado!=-1)//lleva anidamiento: ExcepcionDivisionPorCero
System.out.println("ExcepcionDivisionPorCero: "
+ re.getMessage().substring(indiceAnidado+1));
//imprimimos solo el mensaje anidado
else //no lleva anidamiento: RemoteException
System.out.println("RemoteException" + re);
}
catch (NotBoundException nbe) {
System.out.println("NotBoundException" + nbe);
}
catch (ArithmeticException ae) {
System.out.println("java.lang.ArithmeticException"+ ae);
}
catch (Exception e) {
System.out.println("java.lang.Exception" + e);
}
}
}

En caso de que el servidor se encuentre en otra máquina diferente del


cliente, solo es necesario cambiar la URL de RMIRegistry. Por ejemplo,
si la dirección IP de la otra máquina fuera 201.10.10.1, la URL sería
rmi://201.10.10.1:1099/ServicioCalculadora (o bien cambiamos la
IP por su nombre DNS equivalente).
CC-BY-SA • PID_00187406 43 Java RMI

3.2. Compilar y ejecutar

Una vez completado el código fuente de la aplicación, vamos a compilarla y Ved también
ejecutarla siguiendo una serie de pasos muy simples y ordenados. Ante todo,
En el subapartado "Ejecución
debemos asegurarnos de que todos los archivos de código fuente se encuen- en un entorno distribuido" ve-
tran en el mismo directorio dentro de nuestro sistema. Dada la simplicidad remos cómo podéis organi-
zar los archivos en un entorno
de la aplicación, no es necesario disponerla en diferentes directorios. Estos de ejecución distribuido y más
complejo.
archivos son: Calculadora.java, CalculadoraImpl.java, CalculadoraServidor.java,
CalculadoraCliente.java y el archivo de pólizas java.policy.

Para el seguimiento de la demostración que viene a continuación, trabajare- Nota


mos en modo consola y ejecutaremos por línea de órdenes las herramientas
Trabajaremos independien-
básicas de desarrollo de Java que vienen de serie con el JDK. Primero debemos temente del sistema operati-
crear un directorio de trabajo en nuestro sistema, que podemos denominar vo. Por ejemplo, en Windows,
la consola se activa ejecutan-
CasoEstudioRMI, donde guardaremos todos los archivos de código fuente, do el intérprete de órdenes
cmd.exe. En Mac, ejecutamos
además del archivo de pólizas java.policy. Terminal y en Linux, bash.

3.2.1. Compilar la interfaz remota, servidor y cliente


Nota

En la consola, nos situamos en el directorio de trabajo que hemos creado y Para el trabajo en consola, uti-
lizaremos una notación gené-
ejecutamos por línea de órdenes el compilador de Java javac para compilar rica, que podréis adaptar fácil-
mente a vuestro sistema ope-
todos los archivos de código fuente que hemos creado hasta el momento, co-
rativo.
rrespondientes a la interfaz remota, el servidor y el cliente.

CasoEstudioRMI > javac *.java

3.2.2. Generar stubs y skeletons con rmic

Una vez tenemos todo el código compilado, disponemos de los archivos .class
de código binario en el mismo directorio de trabajo. Entre estos archivos se
encuentra CalculadoraImpl.class, con la implementación de los objetos remo-
tos, a partir de la cual se generan los stubs y skeletons mediante el compilador
de stubs que proporciona el entorno Java rmic.

Como hemos visto con anterioridad, a partir de la versión Java 1.5 ya no es ne- Stubs
cesario utilizar explícitamente rmic para generar los stubs. En lo concerniente
De hecho, los stubs se generan
a los skeletons, tampoco son necesarios, al ser sustituidos por la capacidad refle- siempre durante la ejecución,
xion de Java. Sin embargo, en las últimas versiones de Java se ofrece la opción aunque sea de manera implí-
cita y aunque no generen nin-
de generar tanto los stubs como los skeletons para mantener la compatibilidad gún archivo en nuestro direc-
torio de trabajo.
con versiones anteriores. En esta sección vamos a generar tanto unos como
otros solo para entender mejor su funcionamiento.
CC-BY-SA • PID_00187406 44 Java RMI

Ejecutamos la orden rmic -keep -v1.1 CalculadoraImpl para forzar Ved también
la generación tanto de los stubs como de los skeletons. La opción -v1.1 sir-
Por su valor didáctico, se reco-
ve para forzar la generación del skeleton y hacer compatible la aplicación mienda el estudio del código
con todas las versiones desde Java 1.1. La opción -keep sirve para conservar de los stubs y skeletons junto
con su funcionamiento, expli-
los archivos temporales (stubs y skeletons) en código fuente. Podemos com- cado en el apartado "Arquitec-
tura RMI" del presente módu-
probar en nuestro directorio de trabajo que se han generado los archivos lo.
CalculadoraImplStub.java y CalculadoraImplSkel.java. Como sabemos, estas cla-
ses son necesarias para efectuar la comunicación entre cliente y servidor.

CasoEstudioRMI > javac *.java


CasoEstudioRMI > rmic -keep -v1.1 CalculadoraImpl

3.2.3. Arrancar el registro (RMIRegistry)

En este momento, ya podemos iniciar la ejecución del servidor. Primero es ne- Precaución
cesario arrancar el servicio de registro remoto, que equivale a arrancar RMIRe-
Por precaución, antes de
gistry, para que el servidor pueda registrar el objeto remoto y el cliente pueda arrancar el servicio, inicializa-
acceder a él. remos la variable de entorno
CLASSPATH para evitar pro-
blemas durante el registro de
objetos remotos en caso de
Arrancamos RMIRegistry ejecutando este servicio por línea de órdenes de la que apunte al directorio de
trabajo.
consola mediante la instrucción rmiregistry. Este servicio quedará activo
(ventana de consola abierta) y escuchando por medio del puerto 1099.

Servidor virtual

Al actuar como un servidor virtual, podríamos arrancar RMIRegistry desde cualquier di-
rectorio del sistema.

CasoEstudioRMI > javac *.java


CasoEstudioRMI > rmic -keep -v1.1 CalculadoraImpl
CasoEstudioRMI > SET CLASSPATH=
CasoEstudioRMI > rmiregistry

3.2.4. Ejecutar el servidor

Siguiendo el proceso de ejecución de la parte servidora, una vez tenemos RMI-


Registry en ejecución, es el momento de arrancar el servidor. La ejecución del
servidor conllevará la creación del objeto remoto Calculadora y su registro
en RMIRegistry.

Para llevar a cabo la ejecución, abrimos una segunda consola y nos situamos
de nuevo en nuestro directorio de trabajo. Desde ahí, ejecutamos el servidor
mediante la orden:

CasoEstudioRMI > java -Djava.security.policy=java.policy CalculadoraServidor

La opción -D permite configurar una propiedad del sistema a la que, como


hemos visto en apartados anteriores, asignamos el archivo java.policy, que
debería estar en nuestro directorio de trabajo. De manera similar al registro de
CC-BY-SA • PID_00187406 45 Java RMI

nombres, una vez que se arranca el servidor, queda activo (ventana de consola
abierta) y pendiente para recibir invocaciones de métodos remotos por parte
del cliente.

3.2.5. Ejecutar el cliente

Finalmente, ejecutamos el cliente y se llevarán a cabo las invocaciones a los


métodos de los objetos remotos. En nuestra calculadora remota, el cliente so-
licita cálculos aritméticos mediante la invocación de los métodos remotos res-
pectivos.

Durante la invocación, se envían los operandos como parámetros y el tipo de


operador (+, -, x, /) proporcionados por el usuario al ejecutar el cliente en la
línea de órdenes. Se tienen que pasar tres datos por línea de órdenes: operando1,
operador, operando2.

Para llevar a cabo la ejecución, abrimos una tercera ventana de consola. Nos Precaución
situamos otra vez en nuestro directorio de trabajo y, desde ahí, ejecutamos el
No debemos cerrar ninguna
cliente con esta orden: java CalculadoraCliente 1 + 2. de las dos ventanas de con-
sola que tenemos abiertas, ya
que esto ocasionaría cancelar
El resultado de la ejecución se mostrará en la misma consola que hemos ejecu- el servicio de registro o apagar
el servidor.
tado, bien con el resultado de la operación aritmética, bien con la información
sobre un problema detectado durante la invocación.

CasoEstudioRMI > java CalculadoraCliente 1 + 2


3

Veamos ahora un ejemplo de ejecución problemática del cliente con la orden


java CalculadoraCliente 2 / 0. En esta ocasión, el resultado de la ejecu-
ción no es el resultado de la división, sino la información de la excepción Ex-
cepcionDivisionPorCero que ha lanzado el servidor por haber solicitado
una división por cero. Como se ha comentado antes, esta excepción generada
en el lado del servidor llega al cliente como excepción anidada de RemoteEx-
ception y por el mismo conducto que el resultado de una operación sobre
la calculadora remota.

CasoEstudioRMI > java CalculadoraCliente 1 + 2


3
CasoEstudioRMI > java CalculadoraCliente 2 / 0
ExcepcionDivisionPorCero: 2/0
CC-BY-SA • PID_00187406 46 Java RMI

3.3. Automatización de tareas

(6)
Llegados a este punto, hemos conseguido llevar a cabo todas las tareas nece- Del inglés computer aided softwa-
re engineering.
sarias para compilar y ejecutar una aplicación distribuida en un entorno RMI
local. Esta aplicación nos ha sido útil para mostrar paso a paso todo el proceso
Herramientas CASE
básico que sigue cualquier aplicación RMI desarrollada y ejecutada a mano,
sin la ayuda de herramientas CASE6 de apoyo al desarrollador. Las herramientas�CASE son
herramientas que ayudan a lle-
var a cabo las diferentes activi-
dades de la ingeniería del soft-
A pesar de ser una aplicación trivial, hemos visto que el proceso manual es ware. Las hay muy populares,
relativamente laborioso e implica un mínimo de cinco pasos, en los cuales como Eclipse y NetBeans, que
dan soporte a las actividades
es necesario abrir varias consolas para arrancar y ejecutar la parte servidora y de implementación, desplie-
gue, ejecución y prueba.
cliente. Además, en caso de que quisiéramos lanzar varios clientes al mismo
tiempo, sería necesario abrir una consola nueva por cada nuevo cliente. Todo
ello vuelve muy pesado este proceso manual, especialmente cuando tenemos
que repetirlo varias veces durante un proceso de prueba.

Una manera habitual de automatizar las tareas es crear un archivo por lotes Archivos por lotes
que ejecute el intérprete de órdenes del sistema operativo, con todas las tareas
En Windows, los archivos por
que hemos visto, y además ejecute tantos clientes como sea necesario. De este lotes se denominan batch (ar-
modo, ejecutar este archivo una única vez será equivalente a hacer todos los chivos bat). En Unix se deno-
minan shell scripts.
pasos explicados.

A continuación, crearemos un archivo por lotes que automatice todo el pro- Nota
ceso de compilación y ejecución de la calculadora remota creando dos clientes
Nos basamos en Windows pa-
consecutivos. En función del sistema operativo, pondremos extensión al ar- ra crear el archivo por lotes.
chivo y lo guardaremos en nuestro directorio de trabajo, junto con los archi- Una vez creado, lo ejecutamos
escribiendo el nombre por lí-
vos de código fuente de la aplicación, desde donde ejecutaremos el archivo. nea de órdenes. Para Unix, el
contenido del script y la ejecu-
ción son muy parecidos.
@echo off
rem Archivo calculadora.bat
rem Caso Estudio RMI: Calculadora remota
echo Se está compilando todo el código...
javac *.java
echo Se crean los stubs y skeletons...
rmic -keep CalculadoraImpl
echo Se está inicializando CLASSPATH...
SET CLASSPATH=
echo Se está arrancando rmiregistry...
start rmiregistry
echo Se está arrancando el servidor CalculadoraServidor...
start java -Djava.security.policy=java.policy CalculadorServidor
echo Pausa para dar tiempo a que arranque el servidor
pause
echo Ejecuta un cliente que pide sumar 1 + 2
java CalculadoraCliente 1 + 2
echo Ejecuta otro cliente que pide dividir 2 / 0
java CalculadoraCliente 2 / 0
echo Fin programa
pause
CC-BY-SA • PID_00187406 47 Java RMI

3.4. Ejecución en un entorno distribuido

Hasta ahora, hemos ejecutado el caso de estudio de la calculadora remota en- Entorno distribuido
tre dos JVM en un mismo ordenador (ejecución local). En este subapartado,
Para llevar a cabo la simulación
veremos cómo podemos ejecutar la misma aplicación en un entorno distri- de un entorno distribuido, po-
buido, como mínimo entre dos ordenadores diferentes, uno como cliente y demos considerar, por ejem-
plo, trabajar con el ordenador
otro como servidor. Si tenemos acceso a más de dos ordenadores, podemos de casa y el de la oficina, el or-
denador de un amigo, etc.
acercarnos más a la realidad de las aplicaciones distribuidas conectando varios
clientes con un servidor.

Las características de simplicidad y transparencia que presenta RMI ha-


cen que esta experiencia sea trivial, puesto que solo necesitamos la di-
rección IP del servidor y trasladar los archivos de código a los ordena-
dores correspondientes.

En el caso más simple, para ejecutar la aplicación en dos ordenadores diferen- Dirección IP pública
tes, primero tenemos que decidir qué ordenador representará el rol de cliente
Podéis conseguir la IP públi-
y cuál el de servidor. A continuación, necesitamos la dirección IP pública del ca accediendo desde el or-
ordenador servidor para poder acceder a él. denador servidor a una pági-
na web externa como http://
www.whatismyip.com.

Una vez obtenida la IP pública (supongamos que es 83.36.234.52), modifi-


caremos algunos archivos del código fuente del caso de estudio para conseguir
Precaución
que cliente y servidor se comuniquen haciendo que apunten a la misma di-
rección de RMIRegistry (que se encuentra en el servidor). Por lo tanto, cambia- Si los ordenadores implicados
para hacer la prueba utilizan
remos la URL de RMIRegistry, tanto en el cliente como en el servidor, con la un router, habrá que configu-
rarlo para que permita el paso
IP pública del servidor. de las conexiones por el puer-
to de RMIRegistry (1099). Tam-
bién puede ser necesario des-
En el caso del servidor, editamos el archivo CalculadoraServidor.java y cambia- conectar los cortafuegos para
dejar que pase el tráfico que se
mos la línea: originará durante la ejecución.

java.rmi.Naming.rebind("rmi://localhost:1099/ServicioCalculadora", c);

por:

java.rmi.Naming.rebind("rmi://83.36.234.52:1099/ServicioCalculadora ", c);

En el caso del cliente, editamos CalculadoraCliente.java y actuamos de modo


parecido, cambiando la línea:

Calculadora c = (Calculadora) Naming.lookup("rmi://localhost:1099/ServicioCalculadora");

por:

Calculadora c = (Calculadora) Naming.lookup ("rmi://83.36.234.52:1099/ServicioCalculadora");


CC-BY-SA • PID_00187406 48 Java RMI

Una vez efectuadas tales modificaciones, recopilamos los archivos modifica-


dos y trasladamos todos los archivos de la aplicación a los ordenadores corres-
pondientes, según corresponda al cliente o al servidor. Estos archivos y sus
destinos son:

Servidor Cliente .class

Calculadora.class CalculadoraImpl_stub.class Solo es necesario trasladar el


CalculadoraImpl.class ExcepcionDivisionPorCero.class código binario .class, no hace
CalculadoraServidor.class CalculadoraCliente.class falta mover el código fuente
CalculadoraImpl_stub.class .java, a menos que queramos
CalculadoraImpl_skel.class hacer modificaciones posterio-
res.

La decisión del directorio del sistema donde tienen que ir instalados tanto en
la máquina servidora como en la máquina cliente es totalmente irrelevante. Al
ejecutar RMIRegistry, se procederá a registrar los objetos, y se creará un vínculo,
de forma que el cliente encontrará siempre los objetos remotos en el servidor,
con independencia del directorio donde se encuentren.

En este momento, ya tenemos lo necesario para ejecutar nuestra aplicación


distribuida. Solo hay que recordar que el servidor tiene que estar conectado
a Internet, tener RMIRegistry en ejecución y el servidor (CalculadoraSer-
vidor) ejecutándose. Podemos utilizar el mismo archivo Calculadora.bat para
llevar a cabo todas estas tareas del lado del servidor. Desde el ordenador que
hace el rol de cliente (en el directorio donde tenemos las clases del cliente),
ejecutamos un cliente en una ventana de consola:

CasoEstudioRMI > java CalculadoraCliente 1 + 2

y el resultado de la operación debería mostrarse en la consola.

Una vez conseguido el propósito, vale la pena detenerse un momento para


analizar lo que hemos logrado. Desde el cliente hemos hecho una operación
en la calculadora que no se encuentra en el ordenador del cliente, sino en otro
ordenador distante, con la misma facilidad y percepción que si lo hubiéramos
invocado localmente. Como mucho, percibiremos una velocidad de ejecución
más lenta en comparación con una ejecución local.
CC-BY-SA • PID_00187406 49 Java RMI

Resumen

Hemos visto que la programación distribuida con RMI hace posible el desarro-
llo de aplicaciones distribuidas en Java de manera simple y con un alto grado
de transparencia.

Una de las características más importantes que se han analizado y a la que se


ha dedicado más espacio en este módulo es el funcionamiento del protocolo
de comunicación RMI, basado en la serialización de los datos que se transmi-
ten por la red. Este protocolo resulta fundamental para entender el funciona-
miento tanto del mecanismo de invocación remota RMI como de la comuni-
cación interna de la tecnología de componentes distribuidos de Java EE, que
veremos extensamente en el módulo "Java EE".

Otras características importantes que hemos estudiado son la localización y


activación de objetos remotos, el paso de parámetros, la gestión de los objetos
remotos no referenciados (mecanismo de recogida de objetos distribuidos),
excepciones remotas y el administrador de seguridad para aplicaciones basadas
en RMI.

Por otra parte, hemos estudiado con detalle la arquitectura básica de RMI, lo
que nos ha permitido comprobar una vez más la simplicidad y transparencia
en el funcionamiento de invocación remota de Java, muy cercana a la invo-
cación local.

El caso de estudio nos ha permitido conocer en la práctica los conceptos apren-


didos y debe servirnos como referencia para el desarrollo de aplicaciones dis-
tribuidas con RMI.
CC-BY-SA • PID_00187406 51 Java RMI

Actividades
1. Poned dos ejemplos de beneficios y dos ejemplos de limitaciones al trabajar con Java RMI, a
partir de las características mencionadas en el apartado "Características RMI" de este módulo.

2. Dados los siguientes sistemas de software por desarrollar, indicad en cada caso si es ade-
cuado el uso de Java RMI y, en caso afirmativo, la forma en que podemos aprovechar su
potencial.

a) Un gestor de ventanas en un entorno de escritorio.

b) Un sistema de voto electrónico.

c) Un sistema de gestión del correo electrónico vía web, como por ejemplo Hotmail.

d) Un controlador remoto, por ejemplo, para controlar a distancia la presión de un depósito


de gas.

e) Una aplicación de campus virtual, como por ejemplo el Campus UOC.

f) Un software que gestiona el GPS de un coche.

3. Reimplementad la calculadora remota con los requisitos siguientes:

a) Añadir la operación de raíz cuadrada (hay que controlar la excepción en caso de propor-
cionar números negativos para el cálculo de la raíz cuadrada).

b) Añadir las operaciones inversa (1/x) y %.

c) Hacer operaciones con números racionales.

d) Desplegad la nueva calculadora remota en un entorno real. Escribid un archivo .bat para
la automatización de las tareas de despliegue de la calculadora remota.

4. Una compañía de suministro de gas quiere una aplicación distribuida para controlar al-
gunos parámetros de sus tanques de gas, como la temperatura y la presión. Actualmente, la
compañía controla estos parámetros in situ y modifica (incrementa/decrementa) los valores
de los parámetros según sea necesario.

Con la aplicación distribuida, la compañía quiere hacer un control remoto desde sus oficinas,
para evitar desplazamientos, y también, por el peligro que representa tener que controlar los
parámetros en los tanques. Para ello, vamos a suponer que el tanque de gas dispone de dos
sensores, uno que permite leer y modificar la presión y otro que permite leer y modificar la
temperatura. Los valores de los parámetros tienen que estar siempre en los intervalos [Pmin,
Pmax] y [Tmin, Tmax], respectivamente, donde Pmin, Pmax y Tmin, Tmax son valores críticos. En
caso de que el valor de un parámetro se saliera del intervalo, la alarma se dispararía, lo cual
avisaría a un empleado de intervenir por medio de la aplicación para modificar el valor de
un valor permitido. Utilizad las excepciones remotas anidadas para controlar los valores no
permitidos de los parámetros de presión y temperatura.

5. Implementad un convertidor de temperatura remoto que permita convertir un valor de


la temperatura de Celsius a Fahrenheit y a la inversa: cuando se introduzca en °C, tiene que
convertirlo a °F, y cuando se introduzca en °F, tiene que convertirlo a °C.

Desplegad el convertidor de temperatura remoto en un entorno real. Escribid un archivo .bat


para la automatización de las tareas del despliegue del convertidor de temperatura remoto.

6. Implementad un diccionario remoto para consultar los términos informáticos más comu-
nes. Por ejemplo, si un cliente introduce "CPU", se le devolvería la consulta como "central
processing unit". Usad las excepciones remotas para controlar entradas inexistentes en el dic-
cionario.

7. Implementad un sistema de votación remoto que permita a los clientes de una compañía
responder una pregunta con la respuesta "Sí"/"No". En el lado del servidor deberá ser posible
conocer el número total de votantes, el número de votos "Sí" y el número de votos "No".

Ejercicios de autoevaluación
1. Definid RMI e indicad cuáles son sus objetivos principales.
CC-BY-SA • PID_00187406 52 Java RMI

2. Explicad brevemente la relación entre RMI, sockets y streams.

3. ¿Qué es la serialización, cuál es su función principal y cómo se consigue en Java?

4. ¿Qué tipos de parámetros son posibles en RMI y cómo se hace el paso de parámetros en
cada uno durante una invocación remota?

5. Indicad los elementos básicos que forman la arquitectura RMI.

6. Señalad las principales diferencias entre stubs y skeletons.

7. ¿Qué es una interfaz remota y cómo se consigue en RMI? ¿Qué clase de Java extiende una
clase servidora en RMI?

8. ¿Qué es rmic, para qué sirve y en qué etapa del desarrollo de la aplicación distribuida RMI
se aplica?

9. ¿Cómo se registra un objeto remoto en RMI? ¿Cómo se localiza un objeto remoto en RMI?

10. ¿Qué soluciones proporciona Java dentro de su modelo de seguridad para aplicaciones
distribuidas con RMI?
CC-BY-SA • PID_00187406 53 Java RMI

Solucionario
Ejercicios de autoevaluación

1. RMI representa el modelo de objetos distribuidos que propone Java como solución para
desarrollar aplicaciones distribuidas de manera simple, intuitiva, versátil y transparente.

Los objetivos de RMI son la transparencia, la simplicidad, la seguridad, la portabilidad, la


escalabilidad, la robustez, la eficiencia y la fidelidad al paradigma de orientación a objetos
de Java.

2. En RMI, los datos que pasamos por parámetro durante la invocación de un método remoto
son serializados y convertidos en un stream de bytes por el proceso que invoca. Este proceso
escribe en un socket que retransmite el stream por la red, hasta llegar al socket de destino.

3. La serialización es la acción de codificar un objeto en una secuencia de datos. En el contexto


de RMI, el objetivo de la serialización es la codificación de los datos que hay que transmitir
por la red.

El mecanismo de serialización en Java consiste en convertir clases de objetos en una secuencia


de bytes. Si invertimos este proceso, obtendremos una copia del objeto original a partir de
estos mismos bytes de datos, y haremos lo que se conoce como deserialización.

4. Durante una invocación a un método de un objeto remoto, los parámetros que contiene
la invocación pueden ser de tres tipos: tipos primitivos (int, char, float, etc.), que se pasan
por valor; objetos serializables (o no remotos), que se pasan por valor, y objetos remotos, que
se pasan por referencia.

5. Para llevar a cabo una invocación de un método remoto, la arquitectura básica en el en-
torno RMI está formada por un objeto cliente, los stubs en el lado del cliente, el objeto ser-
vidor y los skeletons en el lado del servidor, los sockets para la comunicación entre stubs y
skeletons, RMIRegistry para la localización del objeto remoto y el protocolo de red TCP/IP en
el nivel de transporte de la red entre la JVM local y la remota.

6. Un stub se encuentra en el lado del cliente y es una representación local de un objeto re-
moto del servidor, que referencia todos los métodos remotos de este objeto. De forma equi-
valente, un skeleton representa un skeleton del objeto remoto y se encuentra en el lado del
servidor.

La función principal de un stub es crear y mantener una conexión de sockets abierta con el
skeleton del servidor y responsabilizarse de la tarea de serializar y deserializar los datos en
el lado del cliente. De forma parecida, el skeleton se responsabiliza del mantenimiento de la
conexión de sockets con el stub, así como de serializar y deserializar los datos en el lado del
servidor.

7. Los clientes de una aplicación distribuida RMI no referencian directamente los objetos
remotos, sino una interfaz remota que es implementada por el objeto servidor. En el caso
más simple, en el servidor hay una clase interfaz remota que extiende la interfaz Remote con
la firma de todos los métodos remotos que pueden ser invocados por el cliente (es decir,
aquellos que lancen RemoteException en su cláusula throws).

8. rmic es un compilador que permite generar los stubs y skeletons. Una vez tenemos todo
el código fuente (interfaz remota, objeto cliente y servidor) compilado y disponemos de los
archivos .class de código binario, a partir de dichos archivos se generan los stubs y skeletons
mediante el compilador de stubs que proporciona el entorno Java rmic.

Aun así, a partir de la versión Java 1.5, ya no es necesario utilizar rmic explícitamente para
generar los stubs. Por lo que respecta a los skeletons, tampoco son necesarios, al ser sustituidos
por la capacidad reflexion de Java. Sin embargo, es recomendable generar tanto los stubs como
los skeletons para mantener la compatibilidad con versiones anteriores.

9. La localización de objetos funciona como un directorio o servicio de nombres: un nombre


lógico o identificador del objeto se corresponde con el stub del objeto. Durante el registro,
el servidor tiene que enviar el stub del objeto al directorio para registrarlo, lo que crea un
vínculo entre un nombre identificador del objeto y el stub de ese objeto. De este modo,
las aplicaciones cliente solo tienen que conocer la localización del directorio y el nombre
identificador del objeto para encontrarlo en tiempo de ejecución.

RMI proporciona de serie un servicio de nombres muy simple llamado RMIRegistry, que per-
mite al servidor grabar los objetos remotos para que los clientes puedan encontrarlos.
CC-BY-SA • PID_00187406 54 Java RMI

10. El modelo de seguridad de Java para aplicaciones distribuidas con RMI es de tres tipos:
administradores de seguridad RMI, cifrado a escala de sockets y permisos personalizados.
CC-BY-SA • PID_00187406 55 Java RMI

Glosario
activación de objetos remotos  f  Proceso que instancia objetos remotos que han sido
previamente desactivados o destruidos. La desactivación de objetos remotos hace el proceso
inverso.

interfaz remota  f  Interfaz de los métodos del objeto remoto que establece una separación
entre las firmas de los métodos y el código de dichos métodos.

invocación local  f  Llamada de un método de un objeto a otro objeto en el contexto de


una misma JVM.

invocación remota  f  Llamada de un método de un objeto que se encuentra en una JVM


a otro objeto que se encuentra en una JVM separada. No es necesario que las dos JVM (o
instancias de la misma JVM) estén separadas físicamente, sino que pueden correr en la misma
máquina.

Java virtual machine   f  Entorno donde se ejecuta el código compilado (bytecode) de Java.
Hay versiones de JVM para la mayoría de los sistemas operativos, lo cual proporciona calidad
de independencia a la plataforma de Java. A partir de crear varias instancias de una misma
JVM, se puede simular una invocación remota en una misma máquina.
Sigla JVM

modelo de objetos distribuidos de Java  m  Sistema distribuido basado en el modelo


cliente-servidor en el que los objetos son administrados por servidores y los clientes invocan
los métodos de estos objetos con una invocación remota.

objeto cliente  m  Objeto que invoca objetos remotos y puede recibir los resultados de las
invocaciones.

objeto remoto  m  Objeto servidor administrado por un servidor que puede ser invocado
por un objeto cliente.

objeto servidor  m  Objeto que gestiona los objetos remotos que los objetos clientes buscan
para invocar sus métodos y toda la infraestructura necesaria para recibir las invocaciones y
devolver los resultados a los objetos clientes.

parámetro por referencia  m  Referencias directas de las instancias de los objetos pasados
por parámetro durante una invocación.

parámetro por valor  m  Copias de las instancias originales de los objetos pasados por
parámetro durante una invocación. Los tipos primitivos de datos también se pasan por valor
durante una invocación.

recolector de basura distribuido  m  Mecanismo automático de eliminación de objetos


en sistemas distribuidos en el que todos los objetos del sistema son visitados constantemente
y se eliminan aquellos que no son referenciados por ningún otro objeto. Este mecanismo es
equivalente al que se usa en entornos locales.

serialización  f  Mecanismo que consiste en convertir clases de objetos en una secuencia


de bytes. Si invertimos este proceso, obtendremos una copia del objeto original a partir de
estos mismos bytes de datos (deserialización).

servicio de nombres o localización  m  Servicio que permite encontrar objetos en un


sistema distribuido y conectar con la máquina servidora donde se encuentra el objeto cuyos
métodos se quieren invocar.

skeleton   m  Fragmento de un objeto que representa un esqueleto del objeto remoto y se


encuentra junto al servidor.

stream   m  Estructura de datos que permite almacenar y recuperar información de manera


secuencial.

stream intermedio  m  Stream que envuelve otros streams existentes (streams primitivos o
intermedios) para ganar en funcionalidad.

stream primitivo  m  Stream que habla con los dispositivos directamente leyendo o escri-
biendo los datos tal como llegan de manera secuencial.
CC-BY-SA • PID_00187406 56 Java RMI

stub   m  Fragmento de un objeto que se encuentra en el lado del cliente y es una represen-
tación local de un objeto remoto del servidor que referencia todos los métodos remotos de
dicho objeto.

transparencia de acceso  f  Capacidad de un sistema de ofrecer siempre las mismas inter-


faces al hacer las llamadas a sus servicios.

transparencia de localización o ubicación  f  Capacidad de un sistema de ofrecer un


servicio sin preocuparse de dónde está ubicado físicamente ese servicio.
CC-BY-SA • PID_00187406 57 Java RMI

Bibliografía
Bibliografía básica

Como bibliografía básica para complementar este módulo, recomendamos las obras siguien-
tes:

Caballé, S.; Xhafa, F. (2008). Programación distribuida en Java con RMI. Madrid: Delta Pu-
blicaciones Universitarias.

Grosso, W. (2001). Java RMI. O'Reilly.

Pitt, E.; McNiff, K. (2001). Java.rmi. The Remote Method Invocation Guide. Addison Wesley.

Bibliografía complementaria

Para complementar algunos aspectos más concretos, recomendamos:

Emmerich, W. (2000). Engineering Distributed Objects. Wiley.

Jaworski, J.; Perrone, P. J. (2001). Seguridad en Java. Madrid: Pearson Alhambra.

Referencias bibliográficas

Oráculo (2011). "RMI". The Java Tutorials. [Fecha de consulta: agosto del 2011]: http://
download.oracle.com/javase/tutorial/rmi/.

Wollrath, A.; Riggs, R.; Waldo, J. (1996). A Distributed Object Model for the Java System.
Sun Microsystems. [Fecha de consulta: enero del 2012]: http://pdos.csail.mit.edu/6.824/pa-
pers/waldo-rmi.pdf.

También podría gustarte