Servidor de Cómputos

En esta página doy las indicaciones para compilar y ejecutar el servidor de cómputos que viene con el tutorial de Java/RMI. El servidor de cómputos es una máquina que permite que los clientes realizen cálculos en el servidor. Este ejemplo es complejo porque en el caso general el servidor de cómputos no tiene accesible en su CLASSPATH el código que el cliente desea ejecutar.

Está compilación/ejecución supone que existe un archivo .jar en donde se encuentran las interfaces y stubs del servidor que el cliente necesecita conocer. En cambio, el código que el cliente necesita enviar al servidor se coloca en una página Web alcanzable por el servidor.

Instalación de JDK1.2

Asegúrese de que JDK1.2 está instalado en las máquinas en donde piensa ejecutar el registro de objetos remotos (rmiregistry), el servidor y los clientes. JDK1.2 debe estar en primer lugar en el path de búsqueda de comandos. Para esto coloque la siguiente línea en el .cshrc (o su equivalente en .xxxrc):

    set path=(/usr/local/java1.2/bin $path)
Reemplace /usr/local/java1.2 por el directorio en donde se encuentra JDK1.2 en su máquina.

Verifique que la versión accesible de java es la 1.2 ejecutando el comando java -version. En Solaris el resultado de este comando es:

    % java -version
    java version "1.2.1"
    Solaris VM (build Solaris_JDK_1.2.1_03, native threads, sunwjit)
Todos los programas que usa este ejemplo deben ser ejecutados con JDK 1.2. No mezcle procesos que usan JDK1.2 con otros que usan JDK1.1.

El servidor

Elija un directorio en donde colocar sólo el código del servidor. Baje los siguientes archivos a ese directorio:

Compilación del Servidor

Compile todos el servidor usando javac:

    % javac ComputeEngine.java
Asegurese de situarse en el directorio en donde bajo los archivos y que la variable CLASSPATH incluye el directorio ".".

Generación de los stubs y skeletons

    % rmic -d . ComputeEngine
    % ls
    ... ComputeEngine_Skel.class ComputeEngine_Stub.class ...
Los stubs y skeletons se encargan de establecer una conexión entre los clientes y un objeto remoto. Los stubs corren en la parte cliente y los skel en el servidor. Observe que los fuentes de stubs y skels son borrados (porque no se necesitan).

Generar una distribución del servidor

Utilice el comando jar para generar un archivo compacto con todas las clases que se necesitan para ejecutar tanto el servidor como los clientes:

    % jar cvf compute.jar *.class
    added manifest
    adding: Compute.class(in = 247) (out= 179)(deflated 27%)
    adding: ComputeEngine.class(in = 1423) (out= 781)(deflated 45%)
    adding: ComputeEngine_Skel.class(in = 1726) (out= 963)(deflated 44%)
    adding: ComputeEngine_Stub.class(in = 3211) (out= 1565)(deflated 51%)
    adding: Task.class(in = 183) (out= 151)(deflated 17%)
    %
Compruebe que el archivo contiene todas las clases necesarias:

    % jar tvf compute.jar
    ...
    %
Este archivo debe ser colocado en cualquier máquina en donde desee lanzar el registro de objetos remotos y en donde va a ejecutar el o los clientes. El archivo compute.jar debe ser especificado en la variable CLASSPATH.

Lanzamiento del registro de objetos remotos

En una ventana de su propia estación (y no en anakena) ejecute el comando rmiregistry. Asegúrese que los stubs del servidor o los clientes sean accesibles por el rmiregistry. Para esto basta ejecutar:

    % setenv CLASSPATH .../compute.jar
    % rmiregistry
En donde "..." es el directorio en donde colocó el archivo compute.jar.

Ejecución del servidor

    % setenv CLASSPATH .../compute.jar
    % java java -Djava.security.policy=.../java.policy ComputeEngine compute
    ComputeEngine bound
El archivo java.policy describe las operaciones que pueden realizar las clases de los clientes que se carguen dinámicamente a partir del Web.

El cliente

Elija un directorio en donde colocar el código de un cliente. Baje los siguientes archivos a ese directorio:

Además no se olvide de instalar el archivo compute.jar para que pueda compilar el cliente.

Compilación del cliente

Compile todos el servidor usando javac:

    % setenv CLASSPATH .:.../compute.jar
    % javac ComputePi.java
Asegúrese de situarse en el directorio en donde bajo los archivos y que la variable CLASSPATH incluye el directorio "." y el compute.jar.

Publicación del código del cliente

En realidad, en este ejemplo sólo se requiere publicar el código de la clase Pi. Sin embargo, en el caso general, pueden ser varias las clases que se necesite enviar al servidor. En tal caso lo más cómodo es crear un archivo .jar con todo el código del cliente y publicarlo en el Web.

Supongamos que Ud. puede publicar archivos en la dirección http://www.dcc.uchile.cl/~jperez/Pi/. Entonces genere la distribución del cliente mediante:

    % jar pi.jar *.class
    added manifest
    adding: ComputePi.class(in = 1207) (out= 720)(deflated 40%)
    adding: Pi.class(in = 1340) (out= 781)(deflated 41%)
    %
Luego, mueva el archivo pi.jar al directorio http://www.dcc.uchile.cl/~jperez/Pi/.

Ejecución del cliente

Los clientes pueden ser ejecutados en cualquier máquina que tenga JDK1.2 y que esté conectada a Internet, por ejemplo en anakena u otra estación. Supongamos que Ud. ejecuta el cliente en la máquina borg.

    % setenv CLASSPATH .../pi.jar:.../compute.jar
    % java -Djava.rmi.server.codebase="http://www.dcc.uchile.cl/~jperez/Pi/pi.jar" \
        ComputePi //borg.dcc.uchile.cl/compute 40
    3.1415926535897932384626433832795028841972
Ahora, entreténgase calculando Pi con 10000 decimales.

Como seguramente Ud. ha encontrado algún error que impide hacer la conexión revise cuidadosamente cada uno de los pasos señalados en esta página. ¡Cuidado! Aún cuando el servidor logró conectarse, el error puede estar en la forma en que lanzó el servidor. Revise también esos pasos.

Envíeme vía mail su error favorito para colocarlo en esta página.

Observaciones

Normalmente, el registro de RMI es compartido por varias aplicaciones y/o usuario. Por lo tanto, el CLASSPATH correspondiente al registro de RMI no tendrá accesible las clases de todos los objetos que se exporten mediante el registro.

La implementación del registro de RMI requiere cargar dinámicamente los stubs correspondientes a los objetos que ahí se registran. Como los stubs serán clases desconocidas para el registro, lo usual es que estos stubs se carguen de la red.

En consecuencia los procesos que requieran exportar objetos mediante el registro de RMI incluirán la cláusula -Djava.rmi.server.codebase=... para señalar el sitio en donde el registro ubicará los stubs. El ejemplo del tutorial de Java/RMI considera ese caso.