Invocación Remota de Métodos (RMI)

Empecemos la tarea de implementar una clase para el motor de c�lculo. En general, la implementaci�n de la clase para 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 un m�todo main en la propia clase de implementaci�n del objeto remoto, o puede ser incluido completamente en otra clase. 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 de nombrado que utilice JNDI).

Abajo podemos ver la implementaci�n completa del motor de c�lculo. La clase engine.ComputeEngine implementa el 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());
            e.printStackTrace();
        }
    }
}

Ahora echaremos una mirada m�s cercana a cada uno de los componentes de la implementaci�n del motor de c�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 objeto remoto) 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 utilizada como superclase para la implementaci�n de objetos remotos. La superclase UnicastRemoteObject suministra implementaci�n para un gran n�mero de m�todos de java.lang.Object (equals, hashCode, toString) para que est�n definidos apropiadamente para objetos remotos. UnicastRemoteObject tambi�n incluye constructores y m�todos est�ticos utilizados para exportar un objeto remoto, es decir, hacer que el objeto remoto pueda recibir llamadas de los clientes.

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

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

Si elegimos extender un objeto remoto de otra clase distinta de UnicastRemoteObject, o, alternativamente, los extendemos de la nueva clase java.rmi.activation.Activatable del JDK 1.2 (utilizada pra construir objetos remotos que puedan ser ejecutados sobre demanda), necesitamos exportar expl�citamente el objeto remoto llamando a uno de los m�todos UnicastRemoteObject.exportObject o Activatable.exportObject desde el constructor de nuestra clase (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 otro interface. La clase ComputeEngine tambi�n contiene algunos m�todos que s�lo pueden ser llamados localmente. El primero de ellos es un constructor para objetos ComputeEngine; el segundo es un m�todo main que es utilizado para 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 clase UnicastRemoteObject. Aunque el constructor de la superclase obtiene la llamada incluso si la omitimos en el constructor de ComputeEngine, la hemos incluido por claridad.

Durante la construcci�n, un objeto UnicastRemoteObject es exportado, lo que significa que est� disponible para aceptar 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 en su clausula throws, por eso el constructor de ComputeEngine tambi�n debe declarar que lanza una RemoteException. 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 se encuentra).

.�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 los interfaces remotos. El interface Compute contiene un s�lo m�todo remoto, executeTask, que se implementa de esta forma.

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

Este m�todo implementa el protocolo entre el ComputeEngine y sus clientes. Los clientes proporcionan al ComputeEngine un objeto Task, que tiene una implementaci�n del m�todo execute de task. El ComputeEngine ejecuta 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. El llamador presumiblemente sabe algo m�s sobre el tipo preciso del Object devuelto y puede tipar el resultado al tipo apropiado.

.�Pasar Objetos en RMI

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

  • 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 un m�todo remoto. La mayor�a de estos objetos (como un descriptor de fichero) encapsulan informaci�n que s�lo tiene sentido 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, que es un proxy del lado del cliente que implementa el conjunto completo de interfaces remotos que implementa el objeto remoto.
  • 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 la serializaci�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 el estado 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 definido en 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 al m�todo executeTask. El receptor no ver�a ni el constructor ComputeEngine ni su m�todo main ni cualquier otro m�todo de java.lang.Object.

En las llamadas a m�todo remotoss, los objetos -par�metos, valores de retorno y excpeciones - que no son objetos remotos 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 ejemplar original.

.�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 utilizado para arrancar el ComputeEngine, y, por lo tanto, necesita hacer la inicializaci�n necesaria para preparar el servidor para aceptar llamadas de los clientes. Este m�todo no es un m�todo remoto, lo que significa que no puede ser llamado desde otra m�quina virtual que no sea la suya. C�mo el m�todo main se declara static, no est� asociado con 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 los recursos del sistema por parte de c�digo no firmado que se ejecute dentro de la m�quina virtual. El controlador de seguridad determina si el c�digo descargado tiene acceso al sistema de ficheros local o puede realizar cualquier otra operaci�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 restriciones aseguran 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, el RMISecurityManager. Este controlador de seguridad fuerza una pol�tica de seguridad similar al controlador de seguridad t�pico de los applets (es decir, es muy conservador con los accesos que permite). Una aplicaci�n RMI podr�a definir y utilizar otra clase SecurityManager que diera un acceso m�s liberal a los recursos del sistema, o, en el 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 remoto ComputeEngine esta listo para aceptar llamadas de los clientes en un puerto an�nimo (elegido por el RMI o por el sistema operativo). Observa que el tipo de la variable engine es Compute, y no ComputeEngine. Esta declaraci�n enfatiza que el interface disponible para los clientes es el interfaceCompute y sus m�todos, no la clase ComputeEngine y sus m�todos.

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

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

El interface java.rmi.Naming es utilizado como un API final para la entrega (o registrado) y b�squeda de objetos remotos en el registro. Una vez registrado un objeto remoto en el registro RMI en el host local, los llamadores de cualquier host pueden busar el objeto remoto por el nombre, obtener su referencia, y luego llamar a los m�todos del objeto. El registro podr�a ser compartido por todos los servidores ejec�tandose en un host, o un proceso servidor individual 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 un nombre Compute, que identifica el objeto remoto en el registro. Luego est� el c�digo necesario para a�adir el nombre al registro RMI que se est� ejecutando en el servidor. Esto se hace despu�s (dentro del bloque try con la sentencia.

Naming.rebind(name, engine);

Al llamar al m�todo rebind se hace una llamada remota al registro RMI del host local. Esta llamada puede provocar que se genera une RemoteException, por eso tenemos que manejar la excepci�n. La clase ComputeEngine maneja 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 objeto remoto.
    • Podr�amos necesitar cambiar el valor de localhost por el nombre o direcci�n IP de nuestro servidor. Si se omite el Host 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 el puerto si un servidor crea un registro en otro puerto que no sea el 1099. El puerto por defecto es �til porque proporciona 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. La implementaci�n de objetos remotos como ejemplares de ComputeEngine nunca abandonan la m�quina virtual en que se crearon, por eso, cuando un cliente realiza un b�squeda en el registro de objetos remotos del servidor, se le devuelve una referencia al stub. Como se explic� anteriormente, los objetos remotos en dichos casos se pasan por referencia, no por valor.
  • Obsevemos que por razones de seguridad, una aplicaci�n puede entregar o eliminar referencias a objetos remotos s�lo en un registro que se ejecute en el mismo host. Esta restricci�n evita que un cliente remoto elimine o sobreesciba cualquier 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 para empezar a manejar llamadas, y sale del m�todo main. No es necesario tener un thread esperando para mantener vivo 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 de ComputeEngine en el registro, �ste es alcanzable por un cliente remoto (�el propio registro!). El sistema RMI tiene cuidado de mantener vivo el proceso ComputeEngine. El ComputeEngine est� disponible para aceptar llamadas y no 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 constructor de la clase ComputeEngine o por la llamada al registro para entregar el nombre del objeto "Compute". En cualquier caso, el programa no puede hacer nada m�s que salir e imprimir un mensaje de error. En algunas aplicaciones distribuidas, es posible recuperar un fallo al hacer una llamada remota. Por ejemplo, la aplicaci�n podr�a elegir otro servidor y continuar con la operaci�n.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO