Manual - Programacion - Java - Rmi

24
TutorJava recomienda... Invocación Remota de Métodos (RMI) Autor-Traductor: Juan Antonio Palos (Ozito) Puedes encontrar la Version Original en Ingles en ( http://java.sun.com) Leer comentarios (0) | Escribir comentario | Puntuación: (1 voto) Vota Indice de contenidos Trabajar con RMI Introducción a las Aplicaciones RMI Ventajas de la Carga Dinámica de Código Interfaces, Objetos y Métodos Remotos Crear Aplicaciones Distribuidas utilizando RMI Diseñar e implementar los componentes de nuestra aplicación distribuida. Compilar los Fuentes y Generar stubs. Hacer accesibles las Clases en la Red. Arrancar la Aplicación. Construir un Motor de Cálculo Genérico Escribir un Servidor RMI Diseñar un Interface Remoto Implementar un Interface Remoto Declarar los Interfaces Remotos que están siendo Implementados Definir el Constructor Proporcionar una Implementación para cada Método Remoto Pasar Objetos en RMI El método main() del Servidor Crear e Instalar un Controlador de Seguridad Poner el Objeto Remoto a Disposición de los Clientes Crear un Programa Cliente Compilar el Ejemplo Construir un Fichero JAR con las Clases de Interfaces Construir las Clases del Servidor Construir las clases del Cliente Ejecutar el Ejemplo Una Nota sobre la Seguridad Arrancar el Servidor Arrancar el Cliente Leer comentarios (0) | Escribir comentario | Puntuación: (1 voto) Vota

Transcript of Manual - Programacion - Java - Rmi

Page 1: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)Autor-Traductor: Juan Antonio Palos (Ozito)Puedes encontrar la Version Original en Ingles en ( http://java.sun.com)

Leer comentarios (0) | Escribir comentario | Puntuación: (1 voto) Vota

Indice de contenidos

Trabajar con RMI●

Introducción a las Aplicaciones RMI

Ventajas de la Carga Dinámica de Código❍

Interfaces, Objetos y Métodos Remotos❍

Crear Aplicaciones Distribuidas utilizando RMI❍

Diseñar e implementar los componentes de nuestra aplicación distribuida.❍

Compilar los Fuentes y Generar stubs.❍

Hacer accesibles las Clases en la Red.❍

Arrancar la Aplicación.❍

Construir un Motor de Cálculo Genérico❍

Escribir un Servidor RMI●

Diseñar un Interface Remoto●

Implementar un Interface Remoto

Declarar los Interfaces Remotos que están siendo Implementados❍

Definir el Constructor❍

Proporcionar una Implementación para cada Método Remoto❍

Pasar Objetos en RMI❍

El método main() del Servidor❍

Crear e Instalar un Controlador de Seguridad❍

Poner el Objeto Remoto a Disposición de los Clientes❍

Crear un Programa Cliente●

Compilar el Ejemplo

Construir un Fichero JAR con las Clases de Interfaces❍

Construir las Clases del Servidor❍

Construir las clases del Cliente❍

Ejecutar el Ejemplo

Una Nota sobre la Seguridad❍

Arrancar el Servidor❍

Arrancar el Cliente❍

Leer comentarios (0) | Escribir comentario | Puntuación: (1 voto) Vota

Page 2: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Trabajar con RMI●

Trabajar con RMI

El sistema de Invocación Remota de Métodos (RMI) de Java permite a un objeto que se estáejecutando en una Máquina Virtual Java (VM) llamar a métodos de otro objeto que está en otra VMdiferente.

Nota:

RMI proporciona comunicación remota entre programas escritos en Java. Si unos de nuestrosprogramas está escrito en otro lenguaje, deberemos considerar la utilización de IDL en su lugar.

Esta sección ofrece una breve descripción del sistema RMI que pasea a través de un ejemplocompleto cliente/servidor que utiliza la capacidades únicas de RMI para cargar y ejecutar tareasdefinidas por el usuario en tiempo de ejecución. El servidor del ejemplo implementa un motor decálculo general. El cliente utiliza el motor de cálculo para calcular el valor del número pi.

Introducción a las Aplicaciones RMI Describe el sistema RMI y lista sus ventajas. Además, estalección proporcionar una descripción de una aplicación típica de RMI, compuesta por un servidor yun cliente, y presenta los términos importantes.

Escribir un Servidor RMI Muestra el código del servidor del motor de cálculo. A través de esteejemplo, aprenderemos cómo diseñar e implementat un servidor RMI.

Crear un Programa Cliente Echa un vistazo a un posible cliente del motor de cálculo y lo utilizapara ilustrar las características importantes de un cliente RMI.

Compilar y Ejecutar el Ejemplo Muestra cómo compilar y ejecutar tanto el servidor del motor decálculo como su cliente.

Page 3: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Introducción a las Aplicaciones RMI

Ventajas de la Carga Dinámica de Código❍

Interfaces, Objetos y Métodos Remotos❍

Crear Aplicaciones Distribuidas utilizando RMI❍

Diseñar e implementar los componentes de nuestra aplicación distribuida.❍

Compilar los Fuentes y Generar stubs.❍

Hacer accesibles las Clases en la Red.❍

Arrancar la Aplicación.❍

Construir un Motor de Cálculo Genérico❍

Introducción a las Aplicaciones RMI

Las aplicaciones RMI normalmente comprenden dos programas separados: un servidor y un cliente.Una aplicación servidor típica crea un montón de objetos remotos, hace accesibles unas referenciasa dichos objetos remotos, y espera a que los clientes llamen a estos métodos u objetos remotos.Una aplicación cliente típica obtiene una referencia remota de uno o más objetos remotos en elservidor y llama a sus métodos. RMI proporciona el mecanismo por el que se comunican y se pasaninformación del cliente al servidor y viceversa. Cuando es una aplicación algunas veces nosreferimos a ella como Aplicación de Objetos Distribuidos.

Las aplicaciones de objetos distribuidos necesitan.

Localizar Objetos RemotosLas aplicaciones pueden utilizar uno de los dos mecanismos para obtener referencias a objetosremotos. Puede registrar sus objetos remotos con la facilidad de nombrado de RMI rmiregistry. Opuede pasar y devolver referencias de objetos remotos como parte de su operación normal.

Comunicar con Objetos RemotosLos detalles de la comunicación entre objetos remotos son manejados por el RMI; para elprogramador, la comunicación remota se parecerá a una llámada estándard a un método Java.

Cargar Bytecodes para objetos que son enviados.Como RMI permite al llamador pasar objetos Java a objetos remotos, RMI proporciona el mecanismonecesario para cargar el código del objeto, así como la transmisión de sus datos.

La siguiente ilustración muestra una aplicación RMI distribuida que utiliza el registro para obtenerreferencias a objetos remotos. El servidor llama al registro para asociar un nombre con un objetoremoto. El cliente busca el objeto remoto por su nombre en el registro del servidor y luego llama aun método. Esta ilustración también muestra que el sistema RMI utiliza una servidor Web existentepara cargar los bytecodes de la clase Java, desde el servidor al cliente y desde el cliente al servidor,para los objetos que necesita.

Page 4: Manual - Programacion - Java - Rmi

El sistema RMI utiliza un servidor Web para cargar los bytecodes de la clase Java, desde el servidoral cliente y desde el cliente al servidor.

Ventajas de la Carga Dinámica de Código

Una de las principales y únicas características de RMI es la habilidad de descargar los bytecodes (osimplemente, código) de una clase de un objeto si la clase no está definida en la máquina virtualdel recibidor. Los tipos y comportamientos de un objeto, anteriormente sólo disponibles en una sólamáquina virtual, ahora pueden ser transmitidos a otra máquina virtual, posiblemente remota. RMIpasa los objetos por su tipo verdadero, por eso el comportamiento de dichos objetos no cambiacuando son enviados a otra máquina virtual. Esto permite que los nuevos tipos sean introducidos enmáquinas virtuales remotas, y así extender el comportamiento de una aplicación dinámicamente. Elejemplo del motor de cálculo de este capítulo utiliza las capacidad de RMI para introducir un nuevocomportamiento en un programa distribuido.

Interfaces, Objetos y Métodos Remotos

Una aplicación distribuida construida utilizando RMI de Java, al igual que otras aplicaciones Java,está compuesta por interfaces y clases. Los interfaces definen métodos, mientras que las clasesimplementan los métodos definidos en los interfaces y, quizás, también definen algunos métodosadicionales. En una aplicación distribuida, se asume que algunas implementaciones residen endiferentes máquinas virtuales. Los objetos que tienen métodos que pueden llamarse por distintasmáquinas virtuales son los objetos remotos.

Un objeto se convierte en remoto implementando un interface remoto, que tenga estascaracterísitcas.

Un interface remoto desciende del interface java.rmi.Remote.●

Cada método del interface declara que lanza una java.rmi.RemoteException además de cualquierexcepción específica de la aplicación.

El RMI trata a un objeto remoto de forma diferente a como lo hace con los objetos no-remotoscuando el objeto es pasado desde una máquina virtual a otra. En vez de hacer una copia de laimplementación del objeto en la máquina virtual que lo recibe, RMI pasa un stub para un objetoremoto. El stub actúa como la representación local o proxy del objeto remoto y básicamente, parael llamador, es la referencia remota. El llamador invoca un método en el stub local que esresponsable de llevar a cabo la llamada al objeto remoto.

Un stub para un objeto remoto implementa el mismo conjunto de interfaces remotos que el objetoremoto. Esto permite que el stub sea tipado a cualquiera de los interfaces que el objeto remotoimplementa. Sin embargo, esto también significa que sólo aquellos métodos definidos en uninterface remoto están disponibles para ser llamados en la máquina virtual que lo recibe.

Crear Aplicaciones Distribuidas utilizando RMI

Cuando se utiliza RMI para desarrollar una aplicación distribuida, debemos seguir estos pasosgenerales.

Diseñar e implementar los componentes de nuestra aplicación distribuida.1.

Compilar los Fuentes y generar stubs.2.

Hacer las clases Accesibles a la Red.3.

Arrancar la Aplicación.4.

Diseñar e implementar los componentes de nuestra aplicación distribuida.

Primero, decidimos la arquitectura de nuestra aplicación y determinamos qué componentes sonobjetos lcoales y cuales deberían ser accesibles remotamente. Este paso incluye.

Definir los Interfaces Remotos. Un interface remoto especifica los métodos que pueden ser llamadosremotamente por un cliente. Los clientes programan los interfaces remotos, no la implementación delas clases de dichos interfaces. Parte del diseño de dichos interfaces es la determinación de cualquierobjeto local que sea utilizado como parámetro y los valores de retorno de esos métodos; si alguno deesos interfaces o clases no existen aún también tenemos que definirlos.

Implementar los Objetos Remotos. Los objetos remotos deben implementar uno o varios interfacesremotos. La clase del objeto remoto podría incluir implementaciones de otros interfaces (locales oremotos) y otros métodos (que sólo estarán disponibles localmente). Si alguna clase local va a serutilizada como parámetro o cómo valor de retorno de alguno de esos métodos, también debe serimplementada.

Implementar los Clientes. Los clientes que utilizan objetos remotos pueden ser implementadosdespués de haber definido los interfaces remotos, incluso después de que los objetos remotos hayansido desplegados.

Page 5: Manual - Programacion - Java - Rmi

Compilar los Fuentes y Generar stubs.

Este es un proceso de dos pasos. En el primer paso, se utiliza el compilador javac para compilar losficheros fuentes de Java, los cuales contienen las implementaciones de los interfaces remotos, lasclases del servidor, y del cliente. En el segundo paso es utilizar el compilador rmic para crear losstubs de los objetos remotos. RMI utiliza una clase stub del objeto remoto como un proxy en elcliente para que los clientes puedan comunicarse con un objeto remoto particular.

Hacer accesibles las Clases en la Red.

En este paso, tenmos que hacer que todo - los ficheros de clases Java asociados con los interfacesremotos, los stubs, y otras clases que necesitemos descargar en los clientes - sean accesibles através de un servidor Web.

Arrancar la Aplicación.

Arrancar la aplicación incluye ejecutar el registro de objetos remotos de RMI, el servidor y el cliente.

El resto de este capítulo muestra cómo seguir estos pasos para crear un motor de cálculo.

Construir un Motor de Cálculo Genérico

Esta sección se enfoca a una sencilla pero potente aplicación distribuida llamada motor de cálculo.Este motor de cálculo es un objeto remoto en el servidor que toma tareas de clientes, las ejecuta, ydevuelve los resultados. Las tareas se ejecutan en la máquina en la que se está ejecutando elservidor. Este tipo de aplicación distribuida podría permitir que un número de máquinas clientesutilizaran una máquina potente, o una que tuviera hardware especializado.

El aspecto novedoso del motor de cálculo es que las tareas que ejecuta no necesitan estar definidascuando se escribe el motor de cálculo. Se pueden crear nuevas clases de tareas en cualquiermomento y luego entregarlas el motor de cálculo para ejecutarlas. Todo lo que una tarea requierees que su clase implemente un interface particular. Por eso una tarea puede ser enviada al motorde cálculo y ejecutada, incluso si la clase que define la tarea fue escrita mucho después de que elmotor de cálculo fuera escrito y arrancado. El código necesita conseguir que una tarea seadescargada por el sistema RMI al motor de cálculo, y que éste ejecute la tarea utilizando losrecursos de la máquina en la que está ejecutando el motor de cálculo.

La habilidad para realizar tareas arbitrarias esta permitida por la naturaleza dinámica de laplataforma Java, que se extiende a través de la red mediante RMI. El RMI carga dinámicamente elcódigo de las tareas en la máquina virtual del motor de cálculo y ejecuta la tarea si tener unconocimiento anterior de la clase que implementa la tarea. Una aplicación como ésta que tiene lahabilidad de descargar código dinámicamente recibe el nombre de "aplicación basada encomportamiento". Dichas aplicaciones normalmente requieren infraestructuras que permitanagentes. Con RMI, dichas aplicaciones son parte del macanismo básico de programación distribuidade Java.

Page 6: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Escribir un Servidor RMI●

Escribir un Servidor RMI

El servidor del motor de cálculo acepta tareas de los clientes, las ejecuta, y devuelve los resultados.El servidor está compuesto por un interface y una clase. El interface propociona la definición de losmétodos que pueden ser llamados desde el cliente. Esencialmente, el interface define lo que elcliente ve del objeto remoto. La clase proporciona la implementación.

Diseñar un Interface Remoto Esta página muestra cómo el interface Compute es el pegamento queconecta el cliente y el servidor. También aprenderemos sobre el API de RMI que soporta estacomunicación.

Implementar un Interface Remoto En esta página exploraremos la clase que implementa el interfaceCompute, que implementa un objeto remoto. Esta clase también propociona el resto del código queconfigura el programa servidor: un método main que crea un ejemplar del objeto remoto, loregistra con la facilidad de nombrado, y configura un controlador de seguridad.

Page 7: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Diseñar un Interface Remoto●

Diseñar un Interface Remoto

En el corazón del motor de cálculo hay un protocolo que permite que se le puedan enviar trabajos,el motor de cálculo ejecuta esos trabajos, y los resultados son devueltos al cliente. Este protocoloestá expresado en interfaces soportados por el motor de cálculo y por los objetos que le sonenviados.

El protocolo del motor de cálculo en acción.

Cada uno de los interfaces contiene un sólo método. El interface del motor de cálculo Compute,permite que los trabajos sean enviados al motor, mientras que el interface Task define cómo elmotor de cálculo ejecuta una tarea enviada.

El interface compute.Compute define la parte accesible remotamente - el propio motor de cálculo.Aquí está el interface remoto con su único método.

package compute;

import java.rmi.Remote; import java.rmi.RemoteException;

public interface Compute extends Remote { Object executeTask(Task t) throws RemoteException; }

Al extender el interface java.rmi.Remote, este interface se marca a sí mismo como uno deaquellos métodos que pueden ser llamados desde cualquier máquina virtual. Cualquier objeto queimplemente este interface se convierte en un objeto remoto.

Como miembro de un interface remoto, el método executeTask es un método remoto. Por lotanto, el método debe ser definido como capaz de lanzar una java.rmi.RemoteException. Estaexcepción es lanzada por el sistema RMI durante una llamada a un método remoto para indicar queha fallado la comunicación o que ha ocurrido un error de protocolo. Una RemoteException es unaexcepción chequeada, por eso cualquier método Java que haga una llamada a un método remotornecesita manejar esta excepción, capturándola o declarándola en sus clausula throws.

El segundo interface necesitado por el motor de cálculo define el tipo Task. Este tipo es utilizadocomo un argumento del método executeTask del interface Compute. El interface compute.Taskdefine el interface entre el motor de cálculo y el trabajo que necesita hacer, proporcionando laforma de iniciar el trabajo.

package compute;

import java.io.Serializable;

public interface Task extends Serializable { Object execute(); }

Page 8: Manual - Programacion - Java - Rmi

El interface Task define un sólo método, execute. Este método devuelve un Object, y no tieneparámetros ni lanza excepciones. Como este interface no extiende Remote, el método no necesitalistar java.rmi.RemoteException en su clausula throws.

El valor de retorno de los métodos executeTask de Compute y execute de Task es declaradocomo del tipo Object. Esto significa que cualquiera tarea que quiera devolver un valor de uno de lostipos primitivos de Java (como un int o un float) necesita crear un ejemplar de la clase envolventeequivalente para ese tipo (como un Integer o un Float) y devolver ese objeto en su lugar.

Observamos que el interface Task extiende el interface java.io.Serializable. El RMI utiliza elmecanismo de serialización de objetos para transportar objetos entre máquinas virtuales.Implementar Serializable hace que la clase sea capaz de convertirse en un stream de bytesauto-descriptor que puede ser utilizado para reconstruir una copia exacta del objeto serializadocuando el objeto es leído desde el stream.

Se pueden ejecutar diferentes tipos de tareas en un objeto Compute siempre que seanimplementaciones del tipo Task. Las clases que implementen este interface pueden contenercualquier dato necesario para el cálculo de la tarea, y cualquier otro método necesario para esecálculo.

Así es cómo RMI hace posible este sencillo motor de cálculo. Como RMI puede asumir que losobjetos Task están escritos en Java, las implementaciones de los objetos Task que anteriormenteeran desconocidas para el motor de cálculo son descargadas por el RMI dentro de la máquina virtualdel motor de cálculo cuando sea necesario. Esto permite a los clientes del motor de cálculo definirnuevos tipos de tareas para ser ejecutadas en el servidor sin necesitar que el código sea instaladoexplícitamente en dicha máquina. Además, como el método executeTask devuelve unjava.lang.Object, cualquier tipo de objeto Java puede ser pasado como valor de retorno en unallamada remota.

El motor de cálculo, implementado por la clase ComputeEngine, implementa el interfaceCompute, permitiendo que diferentes tareas le sean enviadas mediante llamadas a su métodoexecuteTask. Estas tareas se ejecutan utilizando la implementación de task del método execute.El motor de cálculo devuelve los resultados a su llamador a través de su valor de retorno: unObject.

Page 9: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Implementar un Interface Remoto

Declarar los Interfaces Remotos que están siendo Implementados❍

Definir el Constructor❍

Proporcionar una Implementación para cada Método Remoto❍

Pasar Objetos en RMI❍

El método main() del Servidor❍

Crear e Instalar un Controlador de Seguridad❍

Poner el Objeto Remoto a Disposición de los Clientes❍

Implementar un Interface Remoto

Empecemos la tarea de implementar una clase para el motor de cálculo. En general, la implementación de la clasepara un interface remoto debería al menos.

Declarar los Interfaces remotos que están siendo implementados.●

Definir el constructor del objeto remoto.●

Proprorcionar una implementación para cada método remoto de cada interface remoto.●

El servidor necesita crear e instalar los objetos remotos. Este proceso de configuración puede ser encapsulado en unmétodo main en la propia clase de implementación del objeto remoto, o puede ser incluido completamente en otraclase. El proceso de configuración debería.

Crear e instalar un controlador de seguridad.●

Crear uno o más ejemplares del objeto remoto.●

Registrar al menos uno de los objetos remotos con el registro de objetos remotos de RMI (a algún otro servicio denombrado que utilice JNDI).

Abajo podemos ver la implementación completa del motor de cálculo. La clase engine.ComputeEngine implementael interface remoto Compute y también incluye el método main para configurar el motor de cálculo.

package engine;

import java.rmi.*; import java.rmi.server.*;import compute.*;

public class ComputeEngine extends UnicastRemoteObject implements Compute { public ComputeEngine() throws RemoteException { super(); } public Object executeTask(Task t) { return t.execute(); }

public static void main(String[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } String name = "//localhost/Compute"; try { Compute engine = new ComputeEngine(); Naming.rebind(name, engine); System.out.println("ComputeEngine bound"); } catch (Exception e) { System.err.println("ComputeEngine exception: " + e.getMessage());

Page 10: Manual - Programacion - Java - Rmi

e.printStackTrace(); } }}

Ahora echaremos una mirada más cercana a cada uno de los componentes de la implementación del motor decálculo.

Declarar los Interfaces Remotos que están siendo Implementados

La clase que implementa el motor de cálculo se declara como.

public class ComputeEngine extends UnicastRemoteObject implements Compute

Esta declaración indica que la clase implementa el interface remoto Compute (y, por lo tanto, define un objetoremoto) y extiende la clase java.rmi.server.UnicastRemoteObject.

UnicastRemoteObject es una clase de conveniencia, definida en el API público del RMI, que puede ser utilizadacomo superclase para la implementación de objetos remotos. La superclase UnicastRemoteObject suministraimplementación para un gran número de métodos de java.lang.Object (equals, hashCode, toString) para queestén definidos apropiadamente para objetos remotos. UnicastRemoteObject también incluye constructores ymétodos estáticos utilizados para exportar un objeto remoto, es decir, hacer que el objeto remoto pueda recibirllamadas de los clientes.

Una implementación de objeto remoto no tiene porque extender UnicastRemoteObject, y ninguna implementaciónque lo haga debe suministrar las implementaciones apropiadas de los métodos de java.lang.Object. Además, unaimplementación de un objeto remoto debe hacer una llamada explícita a uno de los métodos exportObject deUnicastRemoteObject para que el entorno RMI se de cuenta del objeto remoto para que éste pueda aceptarllamadas.

Al extender UnicastRemoteObject, la ComputeEngine puede ser utilizada pra crear un sólo objeto remoto quesoporte comunicación remota (punto a punto) y que utilice el transporte de comunicación basado en sockets quetiene por defecto el RMI.

Si elegimos extender un objeto remoto de otra clase distinta de UnicastRemoteObject, o, alternativamente, losextendemos de la nueva clase java.rmi.activation.Activatable del JDK 1.2 (utilizada pra construir objetos remotosque puedan ser ejecutados sobre demanda), necesitamos exportar explícitamente el objeto remoto llamando a unode los métodos UnicastRemoteObject.exportObject o Activatable.exportObject desde el constructor de nuestraclase (o cualquier otro método de inicialización, cuando sea apropiado).

El ejemplo del motor de cálculo define un objeto remoto que implementa un sólo interface remoto y ningún otrointerface. La clase ComputeEngine también contiene algunos métodos que sólo pueden ser llamados localmente. Elprimero de ellos es un constructor para objetos ComputeEngine; el segundo es un método main que es utilizadopara crear un objeto ComputeEngine y ponerlo a disposición de los clientes.

Definir el Constructor

La clase ComputeEngine tiene un único constructor que no toma argumentos.

public ComputeEngine() throws RemoteException { super();}

Este constructor sólo llama al constructor de su superclase, que es el constructor sin argumentos de la claseUnicastRemoteObject. Aunque el constructor de la superclase obtiene la llamada incluso si la omitimos en elconstructor de ComputeEngine, la hemos incluido por claridad.

Durante la construcción, un objeto UnicastRemoteObject es exportado, lo que significa que está disponible paraaceptar peticiones de entrada al escuchar las llamadas de los clientes en un puerto anónimo.

Nota:

En el JDK 1.2, podríamos indicar el puerto específico que un objeto remoto utiliza para aceptar peticiones.

El constructor sin argumentos de la superclase,UnicastRemoteObject, declara la excepción RemoteException ensu clausula throws, por eso el constructor de ComputeEngine también debe declarar que lanza unaRemoteException. Esta excepción puede ocurrir durante la construcción si falla el intento de exportar el objeto(debido a que, por ejemplo, no están disponibles los recursos de comunicación o a que la clase stub apropiada no seencuentra).

Proporcionar una Implementación para cada Método Remoto

La clase para un objeto remoto proporciona implementaciones para todos los métodos remotos especificados en losinterfaces remotos. El interface Compute contiene un sólo método remoto, executeTask, que se implementa deesta forma.

public Object executeTask(Task t) { return t.execute();}

Este método implementa el protocolo entre el ComputeEngine y sus clientes. Los clientes proporcionan alComputeEngine un objeto Task, que tiene una implementación del método execute de task. El ComputeEngineejecuta la tarea y devuelve el resultado del método directamente a su llamador.

El método executeTask no necesita saber nada más sobre el resultado del método execute sólo que es un Object.

Page 11: Manual - Programacion - Java - Rmi

El llamador presumiblemente sabe algo más sobre el tipo preciso del Object devuelto y puede tipar el resultado altipo apropiado.

Pasar Objetos en RMI

Los argumentos y los tipos de retorno de los métodos remotos pueden ser de casi cualquier tipo Java, incluyendoobjetos locales, objetos remotos y tipos primitivos. Más precisamente, una entidad de cualquier tipo Java puede serpasada como un argumento o devuelta por un método remoto siempre que la entidad sea un ejemplar de un tipo quesea.

Un tipo primitivo de Java,●

un objeto remoto, o●

un objeto serializable lo que significa que implementa el interface java.io.Serializable●

Unos pocos tipos de objetos no cumplen con estos criterios y por lo tanto no pueden ser pasados ni devueltos por unmétodo remoto. La mayoría de estos objetos (como un descriptor de fichero) encapsulan información que sólo tienesentido en un espacio de dirección única. Muchas clase del corazón Java, incluso algunas de java.lang y java.util,implementan el interface Serializable.

Estas son las reglas que gobiernan el paso y retorno de valores.

Los objetos remotos se pasan esencialmente por referencia. Una referencia a un objeto remoto es realmente un stub, quees un proxy del lado del cliente que implementa el conjunto completo de interfaces remotos que implementa el objetoremoto.

Los objetos locales son pasados por copia utilizando el macanismo de serialización de objetos de Java. Por defecto,todos los campos se copian, excepto aquellos que están marcados como static o transient. El comportamiendo de laserialización por defecto puede ser sobrecargado en una básica clase-por-clase.

Pasar un objeto por referencia (como se hace con los objetos remotos) significa que cualquier cambio hecho en elestado del objeto por el método remoto es reflejado en el objeto remoto original. Cuando se pasa un objeto remoto,sólo aquellos interfaces que son interfaces remotos están disponibles para el receptor, cualquier otro método definidoen la implementación de la clase o definido en un interface no remoto no estará disponible para el receptor.

Por ejemplo, si pasarámos por referencia un ejemplar de la clase ComputeEngine, el receptor tendría acceso sólo almétodo executeTask. El receptor no vería ni el constructor ComputeEngine ni su método main ni cualquier otrométodo de java.lang.Object.

En las llamadas a método remotoss, los objetos -parámetos, valores de retorno y excpeciones - que no son objetosremotos son pasados por valor. Esto significa que se crea una copia del objeto en la máquina virtual del receptor.Cualquier cambio en el estado del objeto en el receptor será reflejado sólo en la copia del receptor, no en el ejemplaroriginal.

El método main() del Servidor

El método más complicado de la implementación de ComputeEngine es el método main. Este método es utilizadopara arrancar el ComputeEngine, y, por lo tanto, necesita hacer la inicialización necesaria para preparar el servidorpara aceptar llamadas de los clientes. Este método no es un método remoto, lo que significa que no puede serllamado desde otra máquina virtual que no sea la suya. Cómo el método main se declara static, no está asociadocon ningún objeto, sino con la clase ComputeEngine.

Crear e Instalar un Controlador de Seguridad

Lo primero que hace el método main es crear e instalar un controlador de seguridad. Éste protege los accesos a losrecursos del sistema por parte de código no firmado que se ejecute dentro de la máquina virtual. El controlador deseguridad determina si el código descargado tiene acceso al sistema de ficheros local o puede realizar cualquier otraoperación privilegiada.

Todos los programas que utilicen RMI deben instalar un controlador de seguridad o el RMI no descargará las clases(las que no se encuentren el el path local) para los objetos que se reciban como parámetros. Estas restricionesaseguran que las operaciones realizadas por el código descargado pasarán a través de unas pruebas de seguridad.

El ComputeEngine utiliza un ejemplo de controlador de seguridad suministrado como parte del RMI, elRMISecurityManager. Este controlador de seguridad fuerza una política de seguridad similar al controlador deseguridad típico de los applets (es decir, es muy conservador con los accesos que permite). Una aplicación RMIpodría definir y utilizar otra clase SecurityManager que diera un acceso más liberal a los recursos del sistema, o, enel JDK 1.2, utilizar un fichero de vigilancia que ofrezca más permisos.

Aquí temos el código que crea e instala el controlador de seguridad.

if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager());}

Poner el Objeto Remoto a Disposición de los Clientes

Luego, el método main crea un ejemplar de ComputeEngine. Esto se hace con la sentencia.

Compute engine = new ComputeEngine();

Como se mencionó anteriormente, este constructor llama al constructor de su superclase UnicastRemoteObject,que exporta el objeto recien creado al sistema RMI. Una vez completada la exportación, el objeto remotoComputeEngine esta listo para aceptar llamadas de los clientes en un puerto anónimo (elegido por el RMI o por elsistema operativo). Observa que el tipo de la variable engine es Compute, y no ComputeEngine. Esta declaraciónenfatiza que el interface disponible para los clientes es el interfaceCompute y sus métodos, no la claseComputeEngine y sus métodos.

Page 12: Manual - Programacion - Java - Rmi

Antes de que un llamador pueda invocar un método de un objeto remoto, debe obtener una referencia al objetoremoto. Este puede hacerse de la misma forma que en que se obtiene cualquier otra referencia en un programaJava, que es obteniéndolo como parte del valor de retorno de un método o como parte de una estructura de datosque contenga dicha referencia.

El sistema proporciona un objeto remoto particular, el registro RMI, para encontrar referencias a objetos remotos. Elregistro RMI es un sencillo servicio de nombrado para objetos remotos que permite a los clientes remotos obteneruna referencia a un objeto remoto por su nombre. El registro se utiliza típicamente para localizar el primer objetoremoto que un cliente RMI necesita utilizar. Este primer objeto remoto, luego proporciona soporte para encontrarotros objetos.

El interface java.rmi.Naming es utilizado como un API final para la entrega (o registrado) y búsqueda de objetosremotos en el registro. Una vez registrado un objeto remoto en el registro RMI en el host local, los llamadores decualquier host pueden busar el objeto remoto por el nombre, obtener su referencia, y luego llamar a los métodos delobjeto. El registro podría ser compartido por todos los servidores ejecútandose en un host, o un proceso servidorindividual podría crear y utilizar su propio registro si así lo desea.

La clase ComputeEngine crea un nombre para el objeto con la sentencia.

String name = "//localhost/Compute";

Este nombre incluye el nombre del host localhost, en el que se están ejecutando el registro y el objeto remoto, y unnombre Compute, que identifica el objeto remoto en el registro. Luego está el código necesario para añadir elnombre al registro RMI que se está ejecutando en el servidor. Esto se hace después (dentro del bloque try con lasentencia.

Naming.rebind(name, engine);

Al llamar al método rebind se hace una llamada remota al registro RMI del host local. Esta llamada puede provocarque se genera une RemoteException, por eso tenemos que manejar la excepción. La clase ComputeEnginemaneja la excepción dentro de los bloques try/catch. Si la excepción no fuese manejada de esta manera,tendríamos que añadir RemoteException a la clausula throws (ahora inexistente) del método main.

Observemos lo siguiente sobre los argumentos de la llamada a Naming.rebind.

El primer parámetro es un java.lang.String formateado como URL representando la localización y el nombre del objetoremoto.

Podríamos necesitar cambiar el valor de localhost por el nombre o dirección IP de nuestro servidor. Si se omite elHost en la URL, el host por defecto es el host local. Tampoco necesitamos especificar el protocolo, Por ejemplo,está permitido suministrar "Compute" como el nombre en la llamada a Naming.rebind.

Opcionalmente se puede suministar un número de puerto en la URL, por ejemplo el nombre"//host:1234/objectname" es legal. Si se omite el puerto, por defecto se toma el 1099. Debemos especificar elpuerto si un servidor crea un registro en otro puerto que no sea el 1099. El puerto por defecto es útil porqueproporciona un lugar bien conocido para buscar los objetos remotos que ofrecen servicios en un host particular.

El sistema RMI susituye una referencia al stub por la referencia real al objeto especificado en el argumento. Laimplementación de objetos remotos como ejemplares de ComputeEngine nunca abandonan la máquina virtual en que secrearon, por eso, cuando un cliente realiza un búsqueda en el registro de objetos remotos del servidor, se le devuelve unareferencia al stub. Como se explicó anteriormente, los objetos remotos en dichos casos se pasan por referencia, no porvalor.

Obsevemos que por razones de seguridad, una aplicación puede entregar o eliminar referencias a objetos remotos sóloen un registro que se ejecute en el mismo host. Esta restricción evita que un cliente remoto elimine o sobreescibacualquier entrada en el registro del servidor.

Una vez que el servidor se ha registrado en el registro RMI local, imprime un mensaje indicando que está listo paraempezar a manejar llamadas, y sale del método main. No es necesario tener un thread esperando para mantenervivo el servidor. Siempre que haya una referencia al objeto ComputeEngine en algún lugar de la máquina virtual(local o remota) el objeto ComputeEngine no será eliminado. Como el programa entrega una referencia deComputeEngine en el registro, éste es alcanzable por un cliente remoto (¡el propio registro!). El sistema RMI tienecuidado de mantener vivo el proceso ComputeEngine. El ComputeEngine está disponible para aceptar llamadas yno será reclamado hasta que.

su nombre sea eliminado del registro, y●

ningún cliente remoto mantenga una referencia al objeto ComputeEngine.●

La pieza final de código del método ComputeEngine.main maneja cualquier excepción que pudiera producirse. Laúnica excepción que podría ser lanzada en el código es RemoteException, que podría ser lanzada por el constructorde la clase ComputeEngine o por la llamada al registro para entregar el nombre del objeto "Compute". Encualquier caso, el programa no puede hacer nada más que salir e imprimir un mensaje de error. En algunasaplicaciones distribuidas, es posible recuperar un fallo al hacer una llamada remota. Por ejemplo, la aplicación podríaelegir otro servidor y continuar con la operación.

Page 13: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Crear un Programa Cliente●

Crear un Programa Cliente

El motor de cálculo es un bonito y sencillo programa - ejecuta las tareas que le son enviadas. Losclientes del motor de cálculo son más complejos. Un cliente necesita llamar al motor de cálculo, perotambién tiene que definir la tarea que éste va a realizar.

Nuestro ejemplo está compuesto por dos clases separadas. la primera clase ComputePi, busca yllama a un objeto Compute. La segunda clase Pi, implementa el interface Task y define el trabajoque va a hacer el motor de cálcilo. El trabajo de la clase Pi es calcular el valor del número pi, conalgún número de posiciones decimales.

Como recordaremos, el interface no-remoto Task se define de esta forma.

package compute; public interface Task extends java.io.Serializable { Object execute(); }

El interface Task extiende java.io.Serializable por lo que cualquier objeto que lo implementepuede ser serializado por el sistema RMI y enviado a una máquina virtual remota como parte de unallamada a un método remoto. Podríamos haber elegido hacer que la implementación de nuestraclase implementara los interfaces Task y Serializable, y hubiera tenido el mismo efecto. Sinembargo, el único proposito del interface Task es permitir que las implementaciones de esteinterface sean pasadas a objetos Compute, por eso, una clase que implemente el interface Task notiene sentido que también implemente el interface Serializable. Dado esto, hemos asociadoexplícitamente los dos interfaces en el tipo system, asegurando que todos los objetos Task seanserializables.

El código que llama a los métodos del objeto Compute debe obtener una referencia a ese objeto,crear un objeto Task, y luego pedir que se ejecute la tarea. Más adelante veremos la definición de latarea Pi. Un objeto Pi se construye con un sólo argumento, la precisión deseada en el resultado. Elresultado de la ejecución de la tarea es un java.math.BigDecimal que representa el número picalculado con la precisión especificada.

La clase cliente ComputePi.

package client;

import java.rmi.*;import java.math.*;import compute.*;

public class ComputePi { public static void main(String args[]) { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { String name = "//" + args[0] + "/Compute"; Compute comp = (Compute) Naming.lookup(name); Pi task = new Pi(Integer.parseInt(args[1]));

Page 14: Manual - Programacion - Java - Rmi

BigDecimal pi = (BigDecimal) (comp.executeTask(task)); System.out.println(pi); } catch (Exception e) { System.err.println("ComputePi exception: " + e.getMessage()); e.printStackTrace(); } } }

Al igual que el servidor ComputeEngine, el cliente empieza instalando un controlador de seguridad.Esto es necesario porque RMI podría descargar código en el cliente. En este ejemplo, el stubComputeEngine es descargado al cliente. Siempre que el RMI descargue código, debe presentarseun controlador de seguridad. Al igual que el servidor, el cliente utiliza el controlador de seguridadproporcionado por el sistema RMI para este propósito.

Después de llamar al controlador de seguridad, el cliente construye un nombre utilizado para buscarun objeto remoto Compute. El valor del primer argumento de la línea de comandos args[0], es elnombre del host remoto, en el que se están ejecutando los objetos Compute. Usando el métodoNaming.lookup, el cliente busca el objeto remoto por su nombre en el registro del host remoto.Cuando se hace la búsqueda del nombre, el código crea una URL que específica el host donde seestá ejecutando el servidor. El nombre pasado en la llamada a Naming.lookup tiene la mismasíntaxis URL que el nombre pasado a la llamada Naming.rebind que explícamos en páginasanteriores.

Luego, el cliente crea un objeto Pi pasando al constructor de Pi el segundo argumento de la línea decomandos, args[1], que indica el número de decimales utilizados en el cálculo. Finalmente, elcliente llama al método executeTask del objeto remoto Compute. El objeto pasado en la llamada aexecuteTask devuelve un objeto del tipo java.math.BigDecimal, por eso el programa fuerza elresultado a ese tipo y almacena en resultado en la variable result. Finalmente el programa imprimeel resultado.

Flujo de mensajes entre el cliente ComputePi, el rmiregistry, y el ComputeEngine.

Finalmente, echemos un vistazo a la clase Pi. Esta clase implementa el interface Task y cálcula elvalor del número pi con un número de decimales especificado. Desde el punto de vista de esteejemplo, el algoritmo real no es importante (excepto, por supuesto, para la fiabilidad del cálculo).Todo lo importante es que el cálculo consume numéricamene muchos recursos (y por eso es el tipoque cosa que querríamos hacer en un servidor potente).

Aquí tenemos el código de la clase Pi, que implementa Task.

package client;import compute.*;import java.math.*;

public class Pi implements Task {

/** constantes utilizadas en el cálculo de pi*/ private static final BigDecimal ZERO = BigDecimal.valueOf(0); private static final BigDecimal ONE = BigDecimal.valueOf(1); private static final BigDecimal FOUR = BigDecimal.valueOf(4);

/** modo de redondeo utilizado durante el cálculo*/ private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN;

/** número de dígitos tras el punto decimal*/ private int digits;

Page 15: Manual - Programacion - Java - Rmi

/** * Construye una tarea para calcular el núemro pi * con la precisión específicada. */ public Pi(int digits) { this.digits = digits; }

/** * Calcula pi. */ public Object execute() { return computePi(digits); }

/** * Calcula el valor de Pi con el número de decimales especificados. * El valor se calcula utilizando la fórmula de Machin. * * pi/4 = 4*arctan(1/5) - arctan(1/239) * * y una poderoas serie de expansiones de arctan(x) * para una precisión suficiente. */ public static BigDecimal computePi(int digits) { int scale = digits + 5; BigDecimal arctan1_5 = arctan(5, scale); BigDecimal arctan1_239 = arctan(239, scale); BigDecimal pi =arctan1_5.multiply(FOUR).subtract(arctan1_239).multiply(FOUR); return pi.setScale(digits, BigDecimal.ROUND_HALF_UP); }

/** * Calcula el valor, en radianes, de la arcotangente de la * inversa del entero suministrado para el número de decimales. * El valor se calcula utilizando la poderosa serie de * expansiones de arcotangente. * * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + * (x^9)/9 ... */ public static BigDecimal arctan(int inverseX, int scale) { BigDecimal result, numer, term; BigDecimal invX = BigDecimal.valueOf(inverseX); BigDecimal invX2 = BigDecimal.valueOf(inverseX * inverseX);

numer = ONE.divide(invX, scale, roundingMode);

result = numer; int i = 1; do { numer = numer.divide(invX2, scale, roundingMode); int denom = 2 * i + 1; term = numer.divide(BigDecimal.valueOf(denom),

Page 16: Manual - Programacion - Java - Rmi

scale, roundingMode); if ((i % 2) != 0) { result = result.subtract(term); } else { result = result.add(term); } i++; } while (term.compareTo(ZERO) != 0); return result; }}

La característica más interesante de este ejemplo es que el objeto Compute no necesita unadefinición de la clase Pi hasta que se le pasa un objeto Pi como un argumento del métodoexecuteTask. Hasta este punto, el código de la clase se ha cargado por el RMI dentro de lamáquina virtual del objeto Compute, se ha llamado al método execute, y se ha ejecutado el códigode la tarea. El Object resultante (que en el caso de la tarea Pi es realmente un objetojava.math.BigDecimal) es enviado de vuelta al cliente, donde se utiliza para imprimir el resultado.

El hecho de que el objeto Task suministrado calcule el valor de Pi es irrelevante para el objetoComputeEngine. Por ejemplo, también podríamos implementar una tarea que generara un númeroprimo aleatorio utilizando un algoritmo probabilistico. (Esto también consume muchos recursos y portanto es un candidato para ser enviado al ComputeEngine). Este código también podría serdescargado cuando el objeto Task fuera pasado al objeto Compute. Todo lo que el objetoCompute sabe es que cada objeto que recibe implementa el método execute, no sabe (y tampocole interesa) qué hace la implementación.

Page 17: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Compilar el Ejemplo

Construir un Fichero JAR con las Clases de Interfaces❍

Construir las Clases del Servidor❍

Construir las clases del Cliente❍

Compilar el Ejemplo

En un escenario del mundo real donde se desarrollara un servicio como el del motor de cálculo, undesarrollador querría crear un fichero JAR que contenga los interfaces Compute y Task para quelos implementan las clases servidor y para que los utilicen los programas clientes.

Luego, un desarrollador (quizás el mismo que creo el fichero JAR con los interfaces) escribiría unaimplementación del interface Compute y desarrollaría ese servicio en una máquina disponible paralos clientes.

Los desarrolladores de los programas clientes pueden utilizar los interfaces Compute y Task(contenidos en el fichero JAR) e independientemente desarrollar una tarea y un programa clienteque utilice un servicio Compute.

En esta página, aprenderemos cómo crear un fichero JAR, las clases del servidor, y las clases delcliente. Veremos como la clase Pi será descargada al servidor durante la ejecución. Tambiénveremos como el stub remoto ComputeEngine será descargado desde el servidor hasta el clientedurante la ejecución.

El ejemplo separa los interfaces, la implementación de los objetos remotos y el código del cliente entres paquetes diferentes.

compute (Los interfaces Compute y Task)●

engine (Implementación de la clase, el interface y el stub de ComputeEngine)●

client (la implementación del cliente ComputePi y de la tarea Pi)●

Primero construiremos el fichero JAR para proporcionar los interfaces del servidor y del cliente a losdesarrolladores.

Construir un Fichero JAR con las Clases de Interfaces

Primero necesitamos compilar los ficheros fuente de los interfaces del paquete compute queconstruir un fichero JAR que contenga los ficheros class. Supongamos que el usuario waldo haescrito estos interfaces particulares y ha situado los ficheros fuente enc:\home\waldo\src\compute (en UNIX sería, /home/waldo/src/compute). Con estos pathspodemos utilizar los siguientes comandos para compilar los intefaces y crear el fichero JAR.

Page 18: Manual - Programacion - Java - Rmi

Detalles específicos de la Plataforma: Construir un Fichero JAR

Windows.

cd c:\home\waldo\srcjavac compute\Compute.java javac compute\Task.javajar cvf compute.jar compute\*.class

UNIX.

cd /home/waldo/srcjavac compute/Compute.java javac compute/Task.javajar cvf compute.jar compute/*.class

El comando jar muestra la siguiente salida (debido a la opción -v).

added manifestadding: compute/Compute.class (in=281) (out=196) (deflated 30%)adding: compute/Task.class (in=200) (out=164) (deflated 18%)

Ahora podemos distribuir el fichero compute.jar a los desarrolladores de las aplicaciones del clientey del servidor para que puedan hacer uso de los interfaces.

En general, cuando cosntruimos las clases del servidor o del cliente con los compiladores javac yrmic, necesitaremos especificar donde deberían residir los ficheros de clase resultantes para quesean accesibles a la red. En este ejemplo, esta localización es, para Unix,/home/user/public_html/classes porque algunos servidores web permiten el acceso apublic_html mediante una URL HTTP construida como http://host/~user/. Si nuestro servidorweb no soporta esta convención, podríamos utilizar un fichero URL en su lugar. El fichero de URLtoma la forma file:/home/user/public_html/classes/ en UNIX, ofile:/c:\home\user\public_html\classes/ en Windows. También se puede seleccionar otro tipode URL apropiado.

La accesibilidad en la red de los ficheros de clases permite al sistema RMI descargar código cuandosea necesario. En vez de definir su propio protocolo para descargar código, RMI utiliza un protocoloURL soportado por Java (por ejemplo, HTTP) para descargar el código. Observa que un servidor webcompleto y poderoso no necesita realizar esta descarga de fichero class. De hecho, un sencilloservidor HTTP proporciona toda la funcionalidad necesaria para hacer que las clases esténdisponibles para su descarga en RMI mediante HTTP, puedes encontrar uno en.

ftp://java.sun.com/pub/jdk1.1/rmi/class-server.zip

Construir las Clases del Servidor

El paquete engine sólo contiene la implementación de la clase del lado del servidor,ComputeEngine, la implementación del objeto remoto del interface Compute. ComoComputeEngine es una implementación de un interface remoto, necesitamos generar un stub parael objeto remoto para que los clientes puedan contactar con él.

Digamos que, ana, la desarrolladora de la clase ComputeEngine, ha situadoComputeEngine.java en el directorio c:\home\ana\src\engine, y ha colocado el fichero classpara que lo usen los clientes en un subdirectorio de su directorio public_html,c:\home\ana\public_html\classes (en UNIX podría ser /home/ana/public_html/classes,accesible mendiante algún servidor web como http://host/~ana/classes/).

Asumamos que el fichero compute.jar esta localizado en el directorioc:\home\ana\public_html\classes. Para compilar la clase ComputeEngine, nuestro path declases debe incluir el fichero compute.jar y el propio directorio fuente.

Una nota sobre el path de clases:

Normalmente, recomendamos seleccionar el path de clases en la linea de comandos utilizando laopción -classpath. Sin embargo, por varias razones, este ejemplo utiliza la variable de entornoCLASSPATH (porque tanto javac como rmic necesitan un path de clases y la opción -classpathse trata de forma diferente en el JDK 1.1 y el JDK 1.2). Recomendamos que no selecciones elCLASSPATH en un fichero de login o de arranque y que los desactives después de haberterminado con este ejemplo.

Para más información sobre CLASSPATH puedes visitarhttp://java.sun.com/products/jdk/1.2/docs/install.html

Aquí podemos ver cómo seleccionar la variable de entorno CLASSPATH.

Page 19: Manual - Programacion - Java - Rmi

Detalles Específicos de la Plataforma: Selecionar el CLASSPATH

Windows.

set CLASSPATH=c:\home\ana\src;c:\home\ana\public_html\classes\compute.jar

Unix:

setenv CLASSPATH /home/ana/src:/home/ana/public_html/classes/compute.jar

Ahora compilamos el fichero fuente ComputeEngine.java y generamos un stub para la claseComputeEngine y coloca el stub accesible a la red. Para crear el stub (y opcionalmente los ficherosesqueleto) ejecutamos el compilador rmic sobre los nombres totalmente cualificados de las clasesde implementación de los objetos remotos que deberían encontrarse en el path de clases. Elcomando rmic toma uno o más nombres de clase como entrada y produce, como salida, ficheros declases con la forma ClassName_Stub.class (y opcionalmente ClassName_Skel.class). El ficheroesqueleto no será generado si llamamos a rmic con la opción -v1.2. Esta opción sólo deberíautilizarse si todos nuestros clientes van a utilizar el JDK 1.2 o posterior.

Detalles Específicos de la Plataforma: Compilar el Motor de Cálculo y sus Stubs

Windows.

cd c:\home\ana\srcjavac engine\ComputeEngine.javarmic -d . engine.ComputeEnginemkdir c:\home\ana\public_html\classes\enginecp engine\ComputeEngine_*.class c:\home\ana\public_html\classes\engine

Unix.

cd /home/ana/srcjavac engine/ComputeEngine.javarmic -d . engine.ComputeEnginemkdir /home/ana/public_html/classes/enginecp engine/ComputeEngine_*.class /home/ana/public_html/classes/engine

La opción -d le dice al compilador rmic que situe los ficheros de clases generados,ComputeEngine_Stub y ComputeEngine_Skel, en el directorio c:\home\ana\src\engine.También necesitamos poner estos ficheros accesibles en la red, por eso debemos copiarlos en elárea public_html\classes.

Como el stub de ComputeEngine implementa el interface Compute, que referencia al interfaceTask, también necesitamos poner estas clases disponibles en la red. Por eso, el paso final esdesempaquetar el fichero compute.jar en el directorio c:\home\ann\public_html\classes parahacer que los interfaces Compute y Task estén disponibles para su descarga.

Detalles Específicos de la Plataforma: Desempaquetar el Fichero JAR

Windows.

cd c:\home\ana\public_html\classesjar xvf compute.jar

Unix.

cd /home/ana/public_html/classesjar xvf compute.jar

El comando jar muestra esta salida.

created: META-INF/extracted: META-INF/MANIFEST.MFextracted: compute/Compute.classextracted: compute/Task.class

Construir las clases del Cliente

Asumamos que el usuario jones ha creado el código del cliente en el directorioc:\home\jones\src\client y colocará la clase Pi (para que sea descargada por el motor decálculo) en el directorio accesible a la red c:\home\jones\public_html\classes (tambiéndisponible mediante algunos servidores como http://host/~jones/classes/). Las dos clases del

Page 20: Manual - Programacion - Java - Rmi

lado del cliente están contenidas en los ficheros Pi.java y ComputePi.java en el subdirectorioclient.

Para construir el código del cliente, necesitamos el fichero compute.jar que contiene los interfacesCompute y Task que utiliza el cliente. Digamos que el fichero compute.jar está situado enc:\home\jones\public_html\classes. Las clases del cliente se pueden construir así.

Detalles Específicos de la Plataforma: Compilar el Cliente

Windows:

set CLASSPATH=c:\home\jones\src;c:\home\jones\public_html\classes\compute.jarcd c:\home\jones\srcjavac client\ComputePi.javajavac -d c:\home\jones\public_html\classes client\Pi.java

UNIX.

setenv CLASSPATH /home/jones/src:/home/jones/public_html/classes/compute.jarcd /home/jones/srcjavac client/ComputePi.javajavac -d /home/jones/public_html/classes client/Pi.java

Sólo necesitamos situar la clase Pi en el directorio public_html\classes\client (el directorioclient lo crea el javac si no existe). Esto es así por esta clase es la única que necesita serdesacargada por la máquina virtual del motor de cálculo.

Ahora podemos ejecutar el servidor y luego el cliente.

Page 21: Manual - Programacion - Java - Rmi

TutorJava recomienda...

Invocación Remota de Métodos (RMI)

En esta página:

Ejecutar el Ejemplo

Una Nota sobre la Seguridad❍

Arrancar el Servidor❍

Arrancar el Cliente❍

Ejecutar el Ejemplo

Una Nota sobre la Seguridad

El modelo de seguridad del JDK 1.2 es más sofisticado que el modelo utilizado en el JDK 1.1.Contiene ampliaciones para seguridad de grano fino y requiere código que permita los permisosespecíficos para realizar ciertas operaciones.

En el JDK 1.1, todo el código que haya en el path de clases se considera firmado y puede realizarcualquier operación, el código descargado está gobernado por las reglas del controlador deseguridad instalado. Si ejecutamos este ejemplo en el JDK 1.2 necesitaremos especificar un ficherode policía cuando ejecutemos el servidor y el cliente. Aquí tenemos un fichero de policía general quepermite al código descargado desde cualquier codebase, hacer dos cosas.

conectar o acceptar conexiones en puertos no privilegiados (puertos por encima del 1024) decualquier host, y

conectar con el puerto 80 (el puerto HTTP).●

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

Si hacemos nuestro código disponible mediante URLs HTTP, deberíamos ejecutar el fichero depolicía anterior cuando ejecutemos este ejemplo. Sin embargo, si utilizarámos un fichero de URLsen su lugar, podemos utilizar el fichero de policía siguiente. Observa que en entornos windows, labarra invertida necesita ser representada con dos barras invertidas en el fichero de policía.

grant { permission java.net.SocketPermission "*:1024-65535", "connect,accept"; permission java.io.FilePermission "c:\\home\\ana\\public_html\\classes\\-", "read"; permission java.io.FilePermission "c:\\home\\jones\\public_html\\classes\\-", "read";};

Este ejemplo asume que el fichero de policía se llama java.policy y contiene los permisosapropiados. Si ejecutamos este ejemplo en el JDK 1.1, no necesitamos un fichero de policía ya queel RMISecurityManager proporciona toda la protección que necesitamos.

Arrancar el Servidor

Antes de arrancar el motor de cálculo, necesitamos arrancar el registro de RMI con el comandormiregistry. Como explicamos en páginas anteriores el registro RMI es una facilidad de nombradoque permite a los clientes obtener una referencia a un objeto remoto.

Page 22: Manual - Programacion - Java - Rmi

Observa que antes de arrancar el rmiregistry, debemos asegurarnos de que el shell o ventana enla que ejecutaremos rmiregistry no tiene la variable de entorno CLASSPATH, o si la tiene ésta noincluye el path a ninguna clase, incluyendo los stubs de nuestras clases de implementación de losobjetos remotos, que querramos descargar a los clientes de nuestros objetos remotos.

Si arrancamos el rmiregistry y éste puede encontrar nuestras clases stub en el CLASSPATH, norecordará que las clases stub cargadas pueden ser cargadas desde el codebase de nuestro servidor(que fue especificado por la propiedad java.rmi.server.codebase cuando se arrancó la aplicaciónservidor). Como resultado, el rmiregistry no enviará a los clientes un codebase asociado con lasclases stub, y consecuentemente, nuestros clientes no podrán localizar y cargar las clases stub (uotras clases del lado del servidor).

Para arrancar el registro en el servidor, se ejecuta el comando rmiregistry. Este comando noproduce ninguna salida y normalmente se ejecuta en segundo plano.

Detalles Específicos de la Plataforma: Arrancar el Registro en el Puerto por Defecto

Windows (utilizar javaw si no está disponible start).

unset CLASSPATHstart rmiregistry

UNIX.

unsetenv CLASSPATHrmiregistry &

Por defecto el registro se ejecuta sobre el puerto 1099. Para arrancar el registro sobre un puertodiferente, se especifica el número de puerto en la línea de comandos. No olvidemos borrar elCLASSPATH.

Detalles Específicos de la Plataforma: Arrancar el Registro en el Puerto 2001

Windows.

start rmiregistry 2001

UNIX.

rmiregistry 2001 &

Una vez arrancado el registro, podemos arrancar el servidor. Primero, necesitamos asegurarnos deque el fichero compute.jar y la implementación del objeto remoto (que es lo que vamos aarrancar) están en nuestro path de clases.

Detalles Específicos de la Plataforma - Seleccionar la variable CLASSPATH

Windows.

set CLASSPATH=c:\home\ana\src;c:\home\ana\public_html\classes\compute.jar

Unix.

setenv CLASSPATH /home/ana/src:/home/ana/public_html/classes/compute.jar

Cuando arrancamos el motor de cálculo, necesitamos especificar, utilizando la propiedadjava.rmi.server.codebase, donde están disponibles las clases del servidor. En este ejemplo, lasclases del lado del servidor disponibles son el stub de ComputeEngine y los interfaces Compute yTask disponibles en el directorio public_html\classes de ana.

Detalles Específicos de la Plataforma: Arrancar el Motor de Cálculo

Windows.

java -Djava.rmi.server.codebase=file:/c:\home\ana\public_html\classes/ -Djava.rmi.server.hostname=zaphod.east.sun.com -Djava.security.policy=java.policy engine.ComputeEngine

UNIX.

java -Djava.rmi.server.codebase=http://zaphod/~ana/classes/ -Djava.rmi.server.hostname=zaphod.east.sun.com -Djava.security.policy=java.policy engine.ComputeEngine

Page 23: Manual - Programacion - Java - Rmi

El comando java anterior define varias propiedades.

java.rmi.server.codebase, una propiedad que especifica una localización, una URL codebase, de lasclases originarias desde este servidor para que la información de las clases enviadas a otras máquinasvirtuales incluya la localización de la clase que el receptor pueda descargar. Si el codebase especificaun directorio (como oposición a un fichero JAR), debemos incluir la barra inclinada en la URL.

java.rmi.server.hostname, una propiedad que indica el nombre totalmente cualificado de nuestroservidor. En algunos entornos de red, el nombre totalmente cualificado del host no se puede obtenerutilizando el API de Java. RMI hace el mejor esfuerzo para obtener ese nombre. Si uno de ellos nopuede ser determinado, fallará y utilizará la dirección IP. Para asegurarnos de que el RMI utilizará unnombre de Host, podríamos seleccionar la propiedad java.rmi.server.hostname como medida deseguridad.

java.security.policy, una propiedad utilizada para especificar el fichero de policía que contiene lospermisos concedidos a los codebases específicados.

La clase stub de ComputeEngine se carga dinámicamente en la máquina virtual del cliente sólocuando la clase no está disponible localmente y la propiedad java.rmi.server.codebase ha sidoconfigurada apropiadamente, para la localización de la clase stub, cuando se arrancó el servidor.Una vez cargada la clase stub no necesitamos recargarla más veces para referencias adicionales aobjetos ComputeEngine.

Arrancar el Cliente

Una vez que el registro y el motor se están ejecutando, podemos arrancar el cliente, especificando.

la localización donde el cliente sirve sus clases (la clase Pi) utilizando la propiedadjava.rmi.server.codebase.

como argumentos de la línea de comandos, el nombre del host (para que el cliente sepa dondelocalizar el objeto remoto) y el número de decimales utilizado en el cálculo del número Pi.

java.security.policy, una propiedad utilizada para especificar el fichero de policía que contiene lospermisos adecuados.

Primero seleccionamos el CLASSPATH para ver el cliente de jones y el fichero JAR que contiene losinterfaces. Luego se arranca el cliente de esta forma.

Detalles Específicos de la Plataforma: Arrancar el Cliente

Windows.

set CLASSPATH c:\home\jones\src;c:\home\jones\public_html\classes\compute.jarjava -Djava.rmi.server.codebase=file:/c:\home\jones\public_html\classes/ -Djava.security.policy=java.policy client.ComputePi localhost 20

UNIX.

setenv CLASSAPTH /home/jones/src:/home/jones/public_html/classes/compute.jarjava -Djava.rmi.server.codebase=http://ford/~jones/classes/ -Djava.security.policy=java.policy client.ComputePi zaphod.east.sun.com 20

Después de arrancar el cliente, deberíamos ver la siguiente salida en nuesta pantalla.

3.14159265358979323846

La siguiente figura muestra de dónde obtienen las clases el rmiregistry, el servidorComputeEngine y el cliente ComputePi durante la ejecución del programa.

Cuando el servidor ComputeEngine coloca su referencia al objeto remoto en el registro, éstedescarga el ComputeEngine_Stub, y también los interfaces Compute y Task de los que la clase

Page 24: Manual - Programacion - Java - Rmi

stub depende. Estas clases son descargadas del servidor web del ComputeEngine (o del sistemade ficheros, dado el caso).

El cliente ComputePi también carga ComputeEngine_Stub, desde el servidor web deComputeEngine, como resultado de la llamada a Naming.lookup. Como el cliente tiene los dosintefaces disponibles en su path de clases, estas clases son cargadas desde allí, no de la localizaciónremota.

Finalmente, la clase Pi se carga en la máquina virtual de ComputeEngine cuado el objeto Pi espasado en la llamada al método remoto executeTask del objeto ComputeEngine. La clase Pi secarga desde la página web del cliente.