0% encontró este documento útil (0 votos)
20 vistas16 páginas

Introducción a Java RMI y su implementación

El documento detalla la implementación de aplicaciones Java RMI, incluyendo la creación de un ejemplo básico 'Hola Mundo Remoto'. Se describen los pasos necesarios para configurar un servidor y un cliente que se comuniquen a través de RMI, así como la importancia de las interfaces remotas y la serialización de objetos. También se menciona la documentación y el código que se debe entregar, junto con las conclusiones sobre las ventajas y desventajas de usar RMI en la invocación de métodos remotos.

Cargado por

n8998y5zyk
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)
20 vistas16 páginas

Introducción a Java RMI y su implementación

El documento detalla la implementación de aplicaciones Java RMI, incluyendo la creación de un ejemplo básico 'Hola Mundo Remoto'. Se describen los pasos necesarios para configurar un servidor y un cliente que se comuniquen a través de RMI, así como la importancia de las interfaces remotas y la serialización de objetos. También se menciona la documentación y el código que se debe entregar, junto con las conclusiones sobre las ventajas y desventajas de usar RMI en la invocación de métodos remotos.

Cargado por

n8998y5zyk
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

COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA

JAVA RMI

Introducción a la programación en RMI de Java

Objetivos
Ejecutar métodos de objetos remotos utilizando RMI de Java. Para cubrir los objetivos de la práctica
tenemos que realizar las siguientes tareas:

• Implementar la aplicación HolaMundoRmi (Hola Mundo Remoto con RMI) en la máquina local,
probando que funciona correctamente para dos sesiones diferentes (una para el servidor y otra
para el cliente).
• Probar la misma aplicación HolaMundoRmi (Hola Mundo Remoto con RMI) en dos o más
computadores diferentes conectados en una red TCP/IP.
• Implementar y probar el correcto funcionamiento de la aplicación dedicada a la “reserva de
habitaciones de un hotel para reuniones”. El proceso general para el desarrollo de una
aplicación Java RMI debe seguir, los siguientes pasos:

1. Definir la interfaz remota. El objeto remoto declara sus servicios implementado la


interfaz remota [Link] y todos los métodos deben declararse como
lanzadores de la excepción [Link].
2. Programar la clase implementación. Ésta es la clase que implementa la interfaz remota
heredando de la clase [Link].
3. Compilar la clase implementación (rmic), generado las clases compiladas stub y
skeleton.
4. Arrancar el registro RMI en el servidor (rmiregistry).
5. Implementar e inicializar los objetos remotos (servidor).
6. Registrar los objetos remotos en el registro RMI, ejecutando el servidor.
7. Implementar, compilar y ejecutar el código del cliente.

Todo esto, al igual que la aplicación HolaMundoRmi (Hola Mundo Remoto con RMI), se puede probar en
la máquina local y luego entre ordenadores diferentes (un servidor y varios clientes) conectados en una
red TCP/IP.

Este ejemplo solo imprime "¡Hola Mundo!" por pantalla, pero sirve como base para entender cómo
hacer un programa ejecutable.

1
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

Documentación a entregar
Código Java (en formato electrónico) correctamente comentado que implemente la aplicación de
“reserva de habitaciones de un hotel para reuniones”, haciendo uso de objetos remotos con RMI de
Java.

Extraer conclusiones de las pruebas realizadas, especificando las ventajas e inconvenientes de trabajar
con RMI de Java para la invocación de métodos remotos.

Descripción
RMI (Remote Meted Invocation) de Java, permite que un objeto que se ejecuta bajo el control de una
máquina virtual Java (JVM) pueda invocar métodos de un objeto que se encuentra bajo el control de una
máquina virtual Java (JVM) diferente. Estas dos JVMs pueden estar ejecutándose como dos procesos
independientes en el mismo computador o, lo que es más importante, estar lanzadas en ordenadores
distintos conectados a través de una red TCP/IP. Es decir, que como Internet es, en último término una
red TCP/IP, lo anterior puede traducirse en que una máquina cliente en cualquier rincón del mundo es
capaz de invocar métodos de un objeto que se encuentra ejecutándose sobre un servidor en cualquier
otra parte del mundo.

La máquina que contiene el objeto cuyos métodos se pueden invocar se llama servidor, y la máquina
que invoca métodos sobre el objeto remoto (también denominado objeto servidor) recibe el nombre de
cliente. En el cliente siempre debe haber una línea de código para recoger la referencia al objeto
remoto. Una vez que el cliente consigue esa referencia, la invocación de métodos sobre el objeto
remoto no difiere en absoluto de la llamada a cualquier método de un objeto local (obviamente, sin
tener en cuenta la velocidad).

El código del servidor debe definir la clase e instanciar un objeto remoto de esa clase. Además de eso, se
debe registrar el objeto remoto y dejar accesibles los métodos a los clientes, para que estos puedan
invocarlos remotamente.

Tanto el cliente como el servidor deben definir, o tener acceso, a una interfaz común, en donde se
declaren los métodos que pueden ser invocados remotamente, y también el controlador de seguridad
que va a tener a su cargo el control de que tanto el servidor como el cliente tengan los niveles de
seguridad adecuados a las acciones que quieren realizar. Es decir, RMI es el modelo de objetos
distribuidos de Java, no añadiendo nuevos elementos al lenguaje y basado en el concepto de interfaz.

2
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

Cuando se invocan métodos sobre objetos remotos, el cliente puede pasar objetos como parámetro y
los métodos de los objetos remotos pueden devolver objetos. Todo esto es posible gracias a la
capacidad de serialización (Serializable) de objetos en Java.

Como programa ejemplo vamos a estudiar el conocido “Hola Mundo!”, pero esta vez como una
aplicación Java RMI, dando lugar a su variante remota natural “Hola Mundo REMOTO!”.
[Link] La implementación de esta “sencilla”
aplicación requiere la creación de cuatro archivos de código fuente y la ejecución de dos utilidades. Con
ello se consigue un mínimo de seis archivos .class que deben ser instalados entre cliente y servidor, y
algunos de ellos en ambos. Al final, deberemos instalar tres archivos .class en el cliente y cinco en el
servidor. Por supuesto, esto es lo mínimo de lo mínimo, la cantidad de archivos dependerá de la
complejidad de la aplicación.

La principal diferencia con respecto al uso local de la aplicación es la intervención de dos programas de
utilidad. La ejecución de uno de ellos en el servidor crea y mantiene un registro de objetos sobre ese
servidor cuyos métodos pueden ser invocados por potenciales clientes. La ejecución del otro programa
de utilidad genera dos archivos especiales denominados stub y skeleton. El archivo stub debe ser
instalado en la máquina cliente y es una representación del objeto remoto; de hecho, el software cliente
se comunica con el stub sobre la máquina cliente, éste se comunica con el skeleton a través de TCP/IP
sobre el servidor, y este skeleton a su vez es el que se comunica con el método del objeto remoto en la
máquina servidor (Figura 1). Por tanto, un stub es un archivo .class que se comporta como el método
invocado el objeto remoto en un lado cliente y como un programa de comunicaciones en el otro
(servidor).

Figura 1. Comunicación entre el objeto cliente y el objeto remoto utilizando RMI de Java.

3
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

Según podemos observar en la Figura 1, RMI requiere de la asistencia de interfaces intermedias,


conocidas con stub y skeleton que actúan a modo de “nodo de distribución” de rutas (proxy) para el
cliente y el servidor, respectivamente. Además RMI requiere que el objeto remoto esté registrado en la
máquina remota (servidor), para lo que debe de estar activo un “servicio de nombres de objetos” en
dicho servidor, activando este servicio la aplicación [Link]. De igual forma, debe estar activo el
servicio de protocolo de red TCP/IP.

El programa cliente ejecuta un método llamado objRemotoHola() sobre un objeto ejecutándose en el


proceso servidor, pasándole la cadena de texto “Mundo REMOTO!”. El método remoto recibe la cadena
de texto que completa anteponiéndole el saludo y se la devuelve al programa cliente que la presentará
en pantalla. El proceso de desarrollo de una aplicación Java RMI debe seguir, en general, los siguientes
pasos:

1. Definir la interfaz remota. El objeto remoto declara sus servicios implementado la interfaz
remota [Link] y todos los métodos deben declararse como lanzadores de la
excepción [Link].
2. Programar la clase implementación. Ésta es la clase que implementa la interfaz remota
heredando de la clase [Link].
3. Compilar la clase implementación (rmic), generado las clases compiladas stub y skeleton.
4. Arrancar el registro RMI en el servidor (rmiregistry).
5. Implementar e inicializar los objetos remotos (servidor).
6. Registrar los objetos remotos en el registro RMI, ejecutando el servidor.
7. Implementar, compilar y ejecutar el código del cliente.

La serie de archivos necesarios para la compilación y ejecución del programa RMI ejemplo “Hola Mundo
REMOTO!” (HolaMundoRmi) es la siguiente:

1. [Link]  Interfaz.
2. [Link]  Objeto.
3. [Link]  Servidor.
4. [Link]  Cliente.
5. [Link] Archivo de pólizas de seguridad.

4
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

La inclusión de un archivo adicional como es [Link], es debido a los requerimientos de seguridad de


la plataforma Java 2, más exigentes y estrictos que en anteriores versiones de JDK. La inclusión del
archivo de pólizas de seguridad es necesaria para garantizar los permisos necesarios para acceder a las
operaciones que se realizan utilizando sockets. Sin este archivo no se podría establecer la comunicación
entre el objeto cliente y el objeto remoto (programa servidor).

En el archivo [Link] se encuentra la compilación del código fuente correspondiente al


servidor y al cliente. A continuación se ejecuta el programa de utilidad [Link] sobre el archivo que
contiene la implementación del objeto remoto para generar los archivos adicionales _Stub y _Skel.
Existe también otro programa de utilidad, [Link], que se ejecuta para registrar los objetos
sobre el servidor, proporcionándole la capacidad de crear y mantener un registro de objetos cuyos
métodos pueden ser invocados remotamente. Antes de lanzar esta utilidad, hay que asegurarse de que
la variable de entorno CLASSPATH actual no está apuntando al directorio que contiene las clases de los
objetos remotos, incluyendo los archivos stub y skel. Si la utilidad encuentra estas clases al arrancar, no
las cargará cuando se ejecute el proceso servidor de los ejemplos, lo cual generará muchos problemas
cuando los clientes intenten descargar las clases desde el servidor remoto.

Las siguientes órdenes liberan la variable de entorno CLASSPATH (devolver al valor original después de
las pruebas, si ésta estuviese fijada) y arrancan el registro RMI sobre el puerto 1099, que es el que se
utiliza por defecto, aunque se puede indicar cualquier otro añadiéndolo al comando.

unset CLASSPATH
start [Link]

El siguiente paso es el lanzar el programa servidor, que podría arrancarse en una ventana diferente si
tanto el cliente y el servidor se ejecutaran en la misma máquina, en procesos separados. Y esto mismo
se debe hacer con el cliente, para que se ejecute como un proceso diferente.

Analicemos ahora cada uno de archivos que forman nuestra primera aplicación distribuida.

El archivo interfaz [Link].

Este archivo es clave para que funcione correctamente nuestra aplicación distribuida. Su contenido es el
siguiente:

5
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

En la definición de la interfaz se extiende la interfaz [Link] (interfaz remota, que es una


interfaz Java que contiene sólo operaciones), que sirve para identificar a todos los objetos remotos.
Cualquier clase que implemente una interfaz remota (extends [Link]) debe heredar directa o
indirectamente de la clase [Link]. Además, cualquier objeto remoto ha de
implementar esta interfaz remota, directa o indirectamente. Solamente aquellos métodos especificados
en una interfaz remota estarán disponibles para ser invocados remotamente. En las clases de
implementación se pueden desarrollar cualquier número de interfaces remotas y se pueden extender
otras clases de implementación.

La interfaz Remote no declara ningún método, pero es imprescindible para que un objeto pueda ser
remoto. En este ejemplo, el objeto remoto que se va a crear implementará la interfaz HolaMundoRmiI,
y por tanto, implementará indirectamente la interfaz Remote por herencia.

Si la clase de la cual se instancia el objeto remoto define métodos que no están declarados en la interfaz
que extiende a Remote, entonces esos métodos no podrán ser invocados de manera remota. El cliente
tiene acceso solamente a aquellos métodos del objeto remoto que estén declarados en la interfaz. En
este caso sólo se declara el método objRemotoHola().

La declaración del método indica, como requisito importante, que se pueden lanzar excepciones del tipo
RemoteException. Esta clase de excepción es la superclase de otros tipos de excepciones que tienen
que ver con problemas que surgen de la librería RMI.

El archivo objeto remoto [Link].

En este archivo java se encuentra la implementación de la interfaz HolaMundoRmiI, implementada


indirectamente la interfaz Remote, a través de la herencia, para satisfacer una de los requisitos
necesarios para que un objeto sea tratado como remoto y se puedan invocar sus métodos
remotamente. Otro requisito es que extienda UnicastRemoteObject, que extiende a su vez la clase
RemoteServer. La declaración de la clase HolaMundoRmiO es la siguiente:

6
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

RemoteServer proporciona un mecanismo general de comunicación para los procesos RMI.


Actualmente sólo soporta UnicastRemoteObject que proporciona un mecanismo de comunicación
punto-a-punto de referencias de objetos activos a través de canales TCP. Es decir, extendiendo esta
clase, el objeto instanciado hereda la posibilidad de constituirse en un punto de comunicación con el
propósito de hacer sus métodos alcanzables desde otros lugares. Es decir, que hay que estar conectado
a una red TCP/IP para que esto funcione correctamente, incluso aunque se ejecuten servidor y clientes
como distintos procesos en la misma máquina.

Dentro de la definición de la clase HolaMundoRmiO nos encontramos el constructor del objeto remoto,
que también debe poder lanzar excepciones del tipo RemoteException. La llamada el método super() es
por poner algo; no es necesaria, ya que cuando se invoca a un constructor sin argumentos esa llamada
se produce por defecto. Y por último tenemos la definición del método que se declaraba en la interfaz, y
que será la que se pueda invocar desde distintos objetos clientes. Este método objRemotoHola sólo
completa el mensaje de saludo y los devuelve.

El objeto cliente y este método son transportados a través de un canal de comunicaciones de dos vías, a
través del cual se pasan objetos en ambas direcciones. Desde luego, un objeto String es muy sencillo y
no representa grandes movimientos de cadenas de una máquina a otra. Sin embargo, incluso este
programa tan simple nos permitirá observar el poder de RMI, ya que los métodos remotos pueden, en
general, recibir y enviar objetos de cualquier tipo por muy complejos que sean, utilizando la
serialización. La serialización de objetos se utiliza para descomponer un objeto en un
array de bytes consistente que puede ser transportado a través de una red TCP/IP y ser reconstruido en
el otro lado partiendo de dicho array de bytes. Esto no es trivial, ya que pueden existir objetos que
contengan objetos, que a su vez contienen objetos y así sucesivamente.

El archivo servidor [Link].

7
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

Este será el archivo que contiene el código para implementar el programa servidor. La declaración de la
clase HolaMundoRmiS, que pasamos a comentar a continuación, es la siguiente:

El primer fragmento de código del método main() instala el controlador de seguridad e intenta
instanciar el objeto remoto dentro del bloque try-catch que recoge la excepción RemoteException, que
es una excepción de comprobación que puede lanzar el objeto remoto y debe ser recogida para que el
programa compile. El controlador de seguridad protege contra el acceso a los recursos del sistema de
código no fiable que se ejecuta dentro de la JVM. Todos los programas que utilicen RMI deben instalar
un controlador de seguridad, o RMI no descargará las clases (fuera de las clases locales) para objetos
recibidos como parámetros, valores de retorno o excepciones en llamadas a métodos remotos. Esta
restricción asegura que las operaciones que realice el código descargado pasan todos los controles de
seguridad.

La sentencia que coloca el objeto remoto en el registro RMI (que actúa como servidor de nombres,
guardando asociaciones del tiempo <nombre, objeto remoto>) y los deja visible al cliente para que éste
pueda invocar sus métodos es: [Link](“ObjetoHola”, objRemoto). El método rebind() es un
método estático de la clase Naming. El mecanismo por el cual se obtienen las referencias a objetos
remotos sigue la sintaxis de las direcciones URL, en donde el protocolo es rmi y el formato de la
referencia es: rmi://servidor:puerto/nombre. Donde rmi es el protocolo, servidor es el nombre o
dirección IP de la máquina que ha registrado el objeto remoto (por defecto la propia máquina), puerto

8
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

es el puerto de comunicaciones del protocolo rmi (por defecto 1099) y nombre es el identificador
asignado al objeto remoto.

En la sentencia rebind() anterior ([Link]("ObjetoHola", objRemoto)), el objeto referenciado por


la variable objRemoto se registra con el nombre ObjetoHola. La clase Naming dispone de otros
métodos, entre los cuales hay varios que permiten obtener información de los objetos que se han
registrado y las direcciones de donde provienen. Por ejemplo, en el cliente se utilizará el método
lookup() para obtener una referencia del objeto remoto.

El resto del código del servidor consiste en la presentación de un mensaje, indicando que el objeto ha
sido registrado y que está listo para ser invocado y la captura de la excepción que se puede generar.

El archivo cliente [Link].

En el archivo cliente está la declaración de la clase HolaMundoRmiC, que consiste solamente en el


método main() y que pasamos a comentar a continuación:

9
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

En la primera sentencia del método main() tenemos la asignación a un objeto String de la dirección URL
del servidor, que luego se completará con el nombre del objeto a la hora de construir la dirección
completa del objeto remoto. Ésta es una sentencia para economizar cuando hay varios objetos remotos,
y en este caso es más ilustrativa que útil.

La siguiente sentencia es la petición al servidor de una referencia al objeto remoto del cual se quieren
invocar métodos. Para ellos, se llama al método estático lookup() de la clase Naming, pasándole la
dirección del objeto como parámetro. Por supuesto, si ellos falla, se lanza una excepción, por lo que la
sentencia es imprescindible que se encuentre dentro de un bloque try-catch para poder compilar. Si
todo ha ido bien, y ya se tiene la referencia al objeto remoto, esta referencia se puede utilizar para
invocar los métodos de ese objeto remoto. Pero antes de hacerlo, hay que tener en cuenta que el
método lookup() devuelve objetos del tipo Remote, por lo que hay que moldear (casting) el objeto a la
interfaz implementada en la clase del objeto (HolaMundoRmiI).
Una vez que ya se tiene la referencia al objeto remoto correcto, debemos de invocar al método
objRemotoHola() sobre ese objeto remoto, pasándole como parámetro una cadena para completar el
mensaje de saludo (“Mundo REMOTO!”), y mostrar el resultado con la cadena de saludo completa. El
resto del código del método main() se centra en la captura y tratamiento de la excepción y la conclusión
del mismo.

Otros aspectos interesantes de Java RMI

Crear un registro RMI en el programa servidor sin utilizar la aplicación rmiregistry.

[Link](1099);

Con esta sentencia se crea el registro RMI sobre el host local y sobre el puerto que se indica, en este
caso el puerto por defecto para RMI (1099). Y a continuación se podrán registrar los objetos remotos
para que sus métodos se puedan invocar remotamente utilizando el método rebind() de la clase
Naming: [Link](“ObjetoRemoro”, objRemoto).

Identificar un cliente que invoca un método.

Método getClientHost del objeto remoto, heredado de la clase [Link].

10
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

Comunicación entre objetos.

Hasta el momento debemos tener claro que un objeto remoto es un objeto instanciado a partir de una
clase que extiende la interfaz Remote (i.e. que implementa la interfaz Remote), bien directa o
indirectamente, y cada método de la interfaz declara que lanza una RemoteException además de
cualquier otra excepción específica de la aplicación. Por el contrario, un objeto normal es un objeto de
una clase que no verifica las dos condiciones anteriores.

Además debemos recordar que el paso de parámetros por valor significa que la función o método recibe
una copia del original, por lo que cualquier modificación en la copia no alterará el original. El paso por
referencia significa que el método recibe una referencia (puntero) al original, de forma que cualquier
cambio en el parámetro estará alterando el original. La situación en Java es que todas las variables de
tipo primitivo se pasan por valor y todos los objetos se pasan por referencia. Sin embargo en RMI, el
matiz es diferente:

• Si un método que es invocado sobre un objeto remoto devuelve un objeto normal, entonces se
serializa una copia del objeto y se envía al cliente que invocó el método. Es decir, el objeto se
devuelve por valor utilizando el mecanismo de serialización de objetos de Java, y por defecto,
todos los campos se copian excepto aquellos que están marcados como static o transient.
Además, si el método en el cliente modifica el objeto, esta modificación no tiene efecto alguno
sobre el objeto original del servidor, sino sólo en la copia del cliente.
• Si un método que es invocado sobre un objeto remoto devuelve un objeto remoto, entonces no
se devuelve una copia serializada del objeto, sino una referencia al objeto (paso por
referencia). Si esta referencia se utiliza para modificar el objeto, se estará modificando a la vez
el objeto remoto original. Realmente, una referencia a un objeto remoto es un stub, que a su
vez es un proxy del lado cliente que implementa el conjunto completo de interfaces remotas
que implementa el objeto remoto.

En definitiva, que hay que ser muy cuidadoso con la invocación de métodos remotos bajo RMI, ya que
pensar que se está modificando un objeto cuando en realidad se está modificando una copia del mismo,
puede conducir a errores considerables.

Serialización de objetos.

RMI utiliza el mecanismo de serialización de objetos para transportar objetos entre máquinas virtuales
de Java (JVM). Implementar Serializable hace que la clase sea capaz de convertirse en un stream de

11
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

bytes auto-descriptor que puede ser utilizado para reconstruir una copia exacta del objeto serializado
cuando el objeto es leído desde el stream.

Uno de los aspectos básicos en los que se apoya RMI es la persistencia de objetos, es decir, en que los
objetos no se destruyan y puedan ser enviados entre máquinas en un entorno distribuido. En muchas
aplicaciones, la persistencia de los datos se controla a través de archivos de texto o bases de datos
comerciales, dependiendo de la complejidad de la aplicación y de los recursos puestos a disposición de
los diseñadores/programadores. Para aplicaciones sencillas los archivos de texto son suficientemente
válidos por su flexibilidad, ya que es muy fácil trabajar con ellos y no están limitados a su uso por un solo
programa. No obstante, no son nada compatibles con los objetos, ya que cuando el formato de un
archivo se complica un poco más allá de una simple tabla o una lista de parámetros (lo que ocurre con
casi todas las aplicaciones orientadas a objetos), el código para manejar esos archivos se vuelve difícil y
ocupa mucho tiempo al programador. Por otro lado, tenemos las bases de datos relacionales y
orientados a objetos que funcionan muy bien con aplicaciones que requieren características de bases de
datos: transacciones, bloqueo de registros, índices, etc. Pero generalmente son costosas, difíciles de
manejar, exigiendo conocimientos avanzados en este campo. En muchos casos, se tiene que
implementar la persistencia con bases de datos. Si un diseño requiere estados en los que hay que
guardar datos, entonces se asume que las bases de datos son una buena elección para implementar la
persistencia. En otros casos, es un archivo con formato orientado a objetos integrado en el propio
entorno de programación.

Una situación similar existe también en el entorno de programación distribuida. Los sockets son flexibles
y fáciles de utilizar, al igual que los archivos, pero presentan los mismos inconvenientes que éstos
cuando se transmiten formatos de datos complejos. Las aplicaciones distribuidas basadas en CORBA, por
ejemplo, disponen de facilidades para transmitir objetos, pero es una solución costosa y difícil de llevar
a cabo.

La serialización de objetos en Java proporciona una solución intermedia para salvar objetos en archivos
y transmitirlos a través de la red. Incluso en grandes aplicaciones que requieran sistemas de gestión de
bases de datos comerciales o comunicaciones middleware, pueden ser un formato para archivos
auxiliares o comunicaciones variadas. Tanto RMI como el API de los JavaBeans utiliza la serialización
para guardar y transmitir objetos. Por lo tanto, en toda aplicación Java en que se vea involucrada la
persistencia o distribución de objetos, la serialización es una poderosa herramienta de programación
que ampliaremos a continuación, aunque RMI nos ofrece la clase
MarshalledObject para gestionar el estado persistente, teniendo un método get que devuelve el mismo
objeto que se le pasa al constructor.

12
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

La serialización de objetos en Java permite escribir y leer objetos en streams, sean éstos archivos o
sockets. Esto proporciona a los programadores una forma sencilla de guardar tanto objetos individuales
como grandes estructuras de objetos en archivos, o enviarlos a través de la red.

Desde la perspectiva del programador Java, gran parte del trabajo de serialización de objetos se realiza
automáticamente. El mecanismo de serialización mantiene control sobre los tipos de los objetos, las
referencias entre ellos y muchos detalles de cómo están almacenados los datos. El API de serialización
está muy estructurado, de tal modo que en muchos casos se puede manejar directamente con facilidad,
mientras permite realizar acondicionamientos muy complejos en caso necesario. A continuación, vamos
a describir brevemente cómo se realizan las tareas básicas de escribir y leer objetos utilizando la
serialización.

En mayoría de los casos, todo lo que se necesita para que un objeto sea serializable es añadir en la
declaración de la clase la implementación de Serializable, tal y como se indica a continuación:

La interfaz Serializable no tiene métodos que deban ser implementados, sin embargo cualquier campo
de datos que implementa esta interfaz también debe ser serializable (todos los tipos básicos como int,
String, Array, Vector, Hashtable, etc., son serializables). El envío de objetos al canal de comunicaciones
se realiza por la clase ObjectOutputStream, que implementa la interfaz genérica ObjectOutput. Esta
interfaz es una extensión, a su vez, de la interfaz DataOutput, que le añade la capacidad de escribir
objetos, así tipos básicos como cadenas y números. El objeto
ObjectOutputStream se crea por encima de cualquier otro ObjectStream, como por ejemplo un archivo
o un socket.

13
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

Ahora podemos utilizar el método writeObject() para enviar datos al canal abierto hacia el archivo de la
siguiente forma:

Y por último podemos volcar todo el contenido y cerrar el canal.

La clase ObjectOutputStream permite escribir un objeto dado en un canal, así como cualquier otro
objeto al que se llegue a través del primero; es decir, permite enviar la estructura de objetos completa.
Cuando se escribe un objeto en un canal, el ObjectOutputStream mantiene todas las referencias entre
objetos, de forma que la complejidad de las estructuras de datos también se mantiene. Del mismo
modo, ObjectOutputStream mantiene controlados todos los tipos de objetos, de forma que se puedan
leer correctamente.

Para leer objetos de una canal de entrada se utiliza la clase ObjectInputStream, para implementar la
interfaz ObjectInput. Del mismo modo que con ObjectOutputStream, un objeto ObjectInputStream se
crea a partir de un InputStream, utilizando el método readObject() para leer un objeto de ese canal.

Para cada objeto que se detecta en el canal, se crea otro nuevo en memoria, rellenando sus datos a
partir del que se lee del canal de entrada. Esto incluye la recuperación de las referencias entre los
objetos almacenados en el stream. Por otro lado, el método readObject() devuelve un Object que debe
ser moldeado (casting), en este caso a un tipo Objeto antes de poder de poder utilizarlo como un objeto
del tipo Objeto. La cuestión estriba en cómo saber el tipo real del objeto; y la respuesta consiste en
solicitar más información al objeto Object devuelto sobre el mismo, utilizando el método getClass(), o
utilizando ciertas capacidades que permite la plataforma Java 2.

A través de la interfaz Serializable la mayoría del trabajo extra que requiere la serialización de objetos se
realiza automáticamente; sin embargo, el programador tiene varias opciones para poder configurar esa
serialización:

14
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

• La palabra clave transient se puede utilizar para definir campos de datos que deben
mantenerse a la hora de escribir en un canal. Cuando se lee un objeto de un canal, los datos de
estos campos transitorios se inicializan a sus valores por defecto, como 0 para los números o
null para las cadenas. El programador puede restaurar los datos transient implementado un
método readObject().
• A un objeto se le pueden incorporar controles adicionales en su proceso de serialización
implementando métodos propios writeObject() y readObject(). Esto puede ser útil para escribir
una versión personalizada del objeto, o simplemente escribir datos adicionales en el canal.

Cuando un ObjectOutputStream escribe un objeto en el canal, siempre busca el método readObject() en


el objeto. Si hay uno, lo utiliza para enviar el objeto al canal. Un objeto que implemente este método
puede utilizar la representación por defecto del objeto para escribir el objeto en el canal, o bien puede
utilizar otros métodos de ObjectOutput, como writeChar() o writeDouble() para enviar los datos al
canal.

En el otro lado de la comunicación, el programado puede implementar un método readObject() propio


para tomar el control de la forma en que se va a leer el objeto del canal. Este método se llama tras
instanciar un nuevo objeto en memoria y antes de leer los datos del canal. De forma similar a
defaultWriteObject() en la escritura, en este caso se puede utilizar el método defaultReadObject() de
ObjectInputStream para leer la representación por defecto del objeto. En los dos casos, estos métodos
solamente son responsables de la escritura y lectura de datos para el objeto en que están definidos, no
para las subclases o superclases.

Actividad práctica: Reserva de habitaciones de un hotel para reuniones.

El sistema de reserva de habitaciones de un hotel para reuniones debe permitir las dos siguientes
operaciones:
• Reserva de habitaciones para reuniones (congresos, presentaciones, celebraciones, etc.),
• Cancelación de habitaciones previamente reservadas.

Dicho hotel dispone de un conjunto de habitaciones, de las cuales dedica un número determinado para
la realización de reuniones. Cada habitación está caracterizada por:
• Nombre de la habitación,
• Capacidad (número máximo de personas que tienen cabida en dicha habitación),

15
COMUNICACIÓN, SINCRONIZACIÓN Y MODELOS DE CONCURRENCIA
JAVA RMI

El sistema de reserva opera sobre un conjunto de habitaciones que el hotel tiene disponibles para poder
reunirse un conjunto de personas y discutir un tema específico, de un día concreto.

Las habitaciones disponibles para el sistema de reservas no están fijadas a priori; es decir, que su estado
(libre o reservada) puede cambiar dinámicamente sin interferir o existir dependencia entre ellas.
Además, cuando se reserva una habitación, debemos de especificar:
• Propósito de la reserva para la reunión,
• Número estimado de asistentes,
• El nombre de la persona que hace la reserva (responsable).

Para una habitación determinada, sólo puede haber una reunión, aunque el propósito de una reunión
puede acaparar varias habitaciones. Además una persona responsable de una reunión, sólo podrá estar
en una habitación determinada. Otra restricción importante a tener en cuenta es que si el número
estimado de asistentes a la reunión supera la capacidad de la habitación que se desea reservar, no
permitir dicha reserva, lanzando un aviso sugiriendo otra “libre” en el mismo día, si la hay.

Además, y para hacer más sencilla la aplicación, no se consideran aspectos de seguridad, y cualquiera
puede cancelar una reserva, aunque previamente ésta haya sido hecha por otra persona diferente.

16

También podría gustarte