Conceptos básicos de rmi

De ChuWiki
Revisión del 10:32 22 mar 2010 de Chudiang (Discusión | contribuciones) (creando stub)

(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Saltar a: navegación, buscar

Conceptos básicos de rmi

En rmi intervienen dos programas java que están ejecutándose en el mismo ordenador o en distintos ordenadores.

Uno de ellos, llamado servidor, "publica" algunas de los objetos/clases que tiene instanciadas. Publicarlos es equivalente a decir que los pone visibles desde la red, de forma que otros programas java puedan llamar a sus métodos.

El otro programa, llamado cliente, localiza en el servidor el objeto publicado y llama a sus métodos. Estos métodos se ejecutan en el programa servidor y devuelven un resultado (por el return habitual) que recoge el cliente en su ordenador.

El cliente se queda "colgado" en la llamada hasta que el servidor termina de ejecutar el código y devuelve un resultado.

Objetos remotos

Los objetos que comparte el serivdor suelen conocerse como objetos remotos. Para hacer un objeto remoto, debemos hacer dos cosas. Primero una interface con los métodos que queremos que se publiquen, es decir, los métodos que queremos que pueda llamar el cliente. Esta interface debe a su vez heredar de la interface Remote. Todos los métodos de esta interface deben lanzar una RemoteException

public interface InterfaceRemota extends java.rmi.Remote
{
    public int suma (int a, int b) throws java.rmi.RemoteException;
}

Luego, debemos hacer el objeto remoto que implemente esta interface. Para evitarnos código, lo mejor es hacer que este objeto remoto herede a su vez de UnicastRemoteObject. Si lo hacemos así, nos obliga a crear un constructor que lanze una RemoteException.

public class ObjetoRemoto extends java.rmi.server.UnicastRemoteObject implements InterfaceRemota 
{
    public ObjetoRemoto() throws java.rmi.RemoteException 
    {
        super();
    }

    public int suma(int a, int b) 
    {
        System.out.println ("sumando "+a+"+"+b + " ...");
        return a+b;
    }
    
}

Creando stub

El objeto remoto se compila de la forma habitual, pero una vez que tenemos el fichero .class, necesitamos pasarle el compilador rmic que viene con java (esta en $JAVA_HOME/bin)

rmic paquete.ObjetoRemoto

No es necesario indicar el .class. Este compilador nos generará un fichero ObjetoRemoto_Stub.class. Esta es la clase que realmente verán los clientes. Es una clase generada automáticamente por java que tiene los mismos métodos que InterfaceRemota, pero que el código en estos métodos simplemente lanza la petición por red al ObjetoRemoto real que está en el servidor y recoge el resultado.

Lanzando rmiregistry

Antes de lanzar nuestro servidor, debemos lanzar un programa llamado rmiregistry que también viene con java (esta en $JAVA_HOME/bin). Este programa es el que realmente sabe todos los objetos que se publican en este ordenador. La llamada Naming.rebind() en realidad conecta con rmiregistry en este ordenador y le avisa que ObjetoRemoto debe publicarse al exterior.

rmiregistry necesita encontrar la clase ObjetoRemoto_Stub.class, así que en el servidor debemos fijar la propiedad java.rmi.server.codebase con el path, en formato url, desde el que rmiregistry puede acceder a esta clase. Esto se puede hacer bien al arrancar el servidor por medio de la opción -D

java -Djava.rmi.server.codebase=file:/un_path/ Servidor

o bien desde código, antes de registrar el objeto remoto

System.setProperty("java.rmi.server.codebase", "file:/un_path/");

Es importante la barra del final, si no, no funciona. También, si hacemos esta clase accesible desde un servidor web, podemos usar algo estilo "http://servidor_web/un_path".

El código en el servidor de rmi

En el servidor de rmi el código que hay realizar es bastante sencillo. Simplemente instanciar el ObjetoRemoto y hacer una llamada a un método de la clase Naming para publicarlo.

try 
{
   System.setProperty("java.rmi.server.codebase", "file:/un_path/");
   ObjetoRemoto objetoRemoto = new ObjetoRemoto();
   Naming.rebind ("ObjetoRemoto", objetoRemoto);
} cacth (Exception e)
{
...
}

El código del cliente rmi.

El código del cliente rmi también es simple. No tiene más que pedir la clase al servidor y guardársela. Luego puede usarla como otra clase cualquiera.

InterfaceRemota objetoRemoto = (InterfaceRemota)Naming.lookup ("//IP_del_Servidor/ObjetoRemoto");
System.out.print ("2 + 3 = ");
System.out.println (objetoRemoto.suma(2, 3));

En el cliente también se puede poner RMISecurityManager y java.policy con las mismas consideraciones anteriores.

El compilado se hace de forma normal. Para compilar necesitaremos el fichero InterfaceRemota.class. Si no hemos puesto RMISecurityManager, necesitarmos los ficheros InterfaceRemota.class y ObjetoRemoto_Stub.class para ejecutar.

Una nota sobre seguridad en el servidor

En otros tutoriales se ven cosas como arrancar un RMISecurityManager y escribir un fichero java.policy de permisos. Normalmente hacer eso sin saber (y en concreto como lo ponen en muchos tutoriales) es peligroso por lo siguiente:

  • Si no instalamos el RMISecurityManager, tenemos una seguridad por defecto que es bastante restrictiva. Si no sabemos, es mejor dejar la de defecto.
  • Si instalamos el RMISecurityManager, estamos habilitando una cosa llamada carga dinámica de clases. La carga dinámica de clases consiste en que un cliente pueda enviarnos una clase hecha por él y que no esté en el servidor. Esa clase puede tener código malicioso.
  • Si instalamos el RMISecurityManager, las clases tanto del servidor como las cargadas dinámicamente de los clientes, tienen permiso para hacer aquellas cosas que se les permite en el fichero java.policy. Es decir, el fichero java.policy dice si una clase concreta tiene o no permisos para escribir o leer en disco duro, establecer sockets, etc. En muchos tutoriales, para evitar problemas en el ejemplo y no extenderse en explicaciones, ponen el fichero java.policy con todos los permisos para todo el mundo. Cualquier clase que venga de un cliente podría borrarnos el disco duro.

Por ello, mi consejo es simplemente que no pongas RMISecurityManager, salvo que necesites carga dinámica de clases. En ese caso, no dejes el fichero java.policy con todos los permisos para todo el mundo.


Enlaces