Invocación Remota de Métodos (RMI)

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

Nuestro ejemplo est� compuesto por dos clases separadas. la primera clase ComputePi, busca y llama a un objeto Compute. La segunda clase Pi, implementa el interface Task y define el trabajo que va a hacer el motor de c�lcilo. El trabajo de la clase Pi es calcular el valor del n�mero pi, con alg�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 implemente puede ser serializado por el sistema RMI y enviado a una m�quina virtual remota como parte de una llamada a un m�todo remoto. Podr�amos haber elegido hacer que la implementaci�n de nuestra clase implementara los interfaces Task y Serializable, y hubiera tenido el mismo efecto. Sin embargo, el �nico proposito del interface Task es permitir que las implementaciones de este interface sean pasadas a objetos Compute, por eso, una clase que implemente el interface Task no tiene sentido que tambi�n implemente el interface Serializable. Dado esto, hemos asociado expl�citamente los dos interfaces en el tipo system, asegurando que todos los objetos Task sean serializables.

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 la tarea Pi. Un objeto Pi se construye con un s�lo argumento, la precisi�n deseada en el resultado. El resultado de la ejecuci�n de la tarea es un java.math.BigDecimal que representa el n�mero pi calculado 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]));
            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 stub ComputeEngine es descargado al cliente. Siempre que el RMI descargue c�digo, debe presentarse un controlador de seguridad. Al igual que el servidor, el cliente utiliza el controlador de seguridad proporcionado por el sistema RMI para este prop�sito.

Despu�s de llamar al controlador de seguridad, el cliente construye un nombre utilizado para buscar un objeto remoto Compute. El valor del primer argumento de la l�nea de comandos args[0], es el nombre del host remoto, en el que se est�n ejecutando los objetos Compute. Usando el m�todo Naming.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 se est� ejecutando el servidor. El nombre pasado en la llamada a Naming.lookup tiene la misma s�ntaxis URL que el nombre pasado a la llamada Naming.rebind que expl�camos en p�ginas anteriores.

Luego, el cliente crea un objeto Pi pasando al constructor de Pi el segundo argumento de la l�nea de comandos, args[1], que indica el n�mero de decimales utilizados en el c�lculo. Finalmente, el cliente llama al m�todo executeTask del objeto remoto Compute. El objeto pasado en la llamada a executeTask devuelve un objeto del tipo java.math.BigDecimal, por eso el programa fuerza el resultado a ese tipo y almacena en resultado en la variable result. Finalmente el programa imprime el 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 el valor del n�mero pi con un n�mero de decimales especificado. Desde el punto de vista de este ejemplo, 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 tipo que 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;
    
    /**
     * 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),
                             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 una definici�n de la clase Pi hasta que se le pasa un objeto Pi como un argumento del m�todo executeTask. Hasta este punto, el c�digo de la clase se ha cargado por el RMI dentro de la m�quina virtual del objeto Compute, se ha llamado al m�todo execute, y se ha ejecutado el c�digo de la tarea. El Object resultante (que en el caso de la tarea Pi es realmente un objeto java.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 objeto ComputeEngine. Por ejemplo, tambi�n podr�amos implementar una tarea que generara un n�mero primo aleatorio utilizando un algoritmo probabilistico. (Esto tambi�n consume muchos recursos y por tanto es un candidato para ser enviado al ComputeEngine). Este c�digo tambi�n podr�a ser descargado cuando el objeto Task fuera pasado al objeto Compute. Todo lo que el objeto Compute sabe es que cada objeto que recibe implementa el m�todo execute, no sabe (y tampoco le interesa) qu� hace la implementaci�n.

COMPARTE ESTE ARTÍCULO

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

SIGUIENTE ARTÍCULO