Java RMI

Java RMI es un framework que nos permite realizar llamadas a métodos remotos. La ventaja es que no tenemos que preocuparnos por los detalles de cómo lograr conectarse a otra JVM, es decir, toda la lógica de redes o de I/O ya viene por defecto implementado en las librerías de JAVA.

RMI significa, Remote Method Invocation o llamado de métodos remotos.

Además, provee toda la infraestructura necesaria para poder realizar llamadas a los métodos remotos, como por ejemplo, el buscador de direcciones para obtener los objetos dentro de esa dirección.

En RMI existen diferentes componentes:

  • Stub: es el encargado de comunicar al cliente con el objeto que se encarga de realizar el trabajo.
  • Skeleton: es el encargado de recibir la solicitud del Stub, transformarla y llamar al método que hace el trabajo necesario.
  • Servicio: es el objeto que utiliza el Skeleton para realizar el trabajo necesario.

Ahora que ya tenemos claro los conceptos de RMI, veamos los pasos generales para su correcta implementación según el libro Head First Design Patterns.

  1. Crear la interfaz del servicio: esta interfaz define los métodos que el cliente puede invocar. El Stub y el servicio deben implementar esta interfaz.
  2. Crear la implementación del servicio: esta es la clase que se encarga de hacer el trabajo
  3. Generar los Stubs y Skeletons por medio de rmic <clase del paso anterior>: como vimos anteriormente, son los utilitarios necesarios para el cliente (Stub) y para el servicio (Skeleton). En las nuevas versiones de JAVA este paso no es necesario siempre y cuando heredemos de la clase UnicastRemoteObject.
  4. Registrar los Stubs por medio de rmiregistry: este paso es vital, ya que registra el servicio para ser accedido remotamente. También se pueden registrar por medio de código como veremos más adelante.
  5. Iniciar el servicio remoto: al crear la instancia del servicio, es posible accederla de manera remota.

Revisemos los pasos anteriores en código.

1. Interfaz del servicio:

Debe heredar la interfaz java.rmi.Remote. Cada método definido en esta interfaz debe indicar que la excepción RemoteException puede enviarse, ya que, al ser un servicio remoto pueden existir problemas de comunicación, de acceso, etc.

Es importante resaltar que tanto los argumentos de los métodos, como los tipos de retorno deben ser serializables. Recuerden que estamos enviando y recibiendo información remotamente.

Todos los tipos primitivos de JAVA son serializables por defecto, es decir, solo si utilizamos parámetros o retornos usando clases creadas por nosotros, debemos marcar esas clases como Serializables implementando la interfaz Serializable.

public interface MyRemoteService extends Remote {

    public String getInfo() throws RemoteException;
}

2. Implementación del servicio:

Debe heredar de UnicastRemoteObject e implementar la interfaz remota que creamos en el paso anterior. En esta clase definimos un String que contiene la información que deseamos acceder remotamente.

Todos los métodos y constructores de esta clase deben indicar que puede ocurrir la excepción RemoteException. En este caso estamos llamando al constructor de UnicastRemoteObject enviando el puerto que vamos a utilizar para acceder a este recurso. De esta forma nos evitamos el paso de rmic que mencionamos anteriormente.

Es vital definir un constructor igual al que expone nuestra clase padre UnicastRemoteObject, en este caso yo decidí utilizar el constructor sin parámetros, sin embargo existe más variaciones de este constructor. Pueden revisar la documentación de esta clase aquí.

public class MyRemoteServiceImpl extends UnicastRemoteObject implements MyRemoteService {
    private String info = "This is a message from remote";

    public MyRemoteServiceImpl() throws RemoteException {
        // this is the port where clients will connect
        super(8080);
    }

    public String getInfo() throws RemoteException {
    }
}

3 . Generar Stubs y Skeletons:

Como mencioné antes, este paso no es necesario si heredamos de UnicastRemoteObject en JAVA 8.

4. Registrar la clase:

Para esto podemos hacer uso de LocateRegistry, que es un Singleton que nos retorna el Registry usando un puerto específico.

Una vez que tenemos el Registry, podemos registrar nuestra clase para que reciba llamados remotos. El método rebind recibe dos parámetros, el URL con el nombre que será usado para acceder a nuestra clase y la instancia de nuestra clase.

static Registry registry = null;

public static void main (String[] args) {
    try {
        registry = LocateRegistry.createRegistry(8080);
        MyRemoteServiceImpl implementation = new MyRemoteServiceImpl();
        registry.rebind("rmi://your_host:8080/name", implementation);
    }
    catch(Exception ex) {
        ex.printStackTrace();
    }
}

Ya que hemos terminado con nuestra clase servidor, podemos comenzar a codificar nuestro cliente. Al igual que en el servidor podemos hacer uso de LocateRegistry y obtenerlo por medio del método getRegistry, el cual recibe como parámetro el host y el puerto.

Para poder acceder a la clase del servidor debemos utilizar el nombre que definimos anteriormente al llamar al método rebind (“rmi://your_host:8080/name”) usando el método lookup.

static Registry registry = null;

public static void main (String[] args) {
    try {
        registry = LocateRegistry.getRegistry("your_host");
        MyRemoteService implementation = (MyRemoteService)registry.lookup("rmi://your_host:8080/name");
        System.out.println(implementation.getInfo());
    }
    catch(Exception ex) {
        ex.printStackTrace();
    }
}

El código de ejemplo de Java RMI es el mismo utilizado en el Patrón Proxy Remoto.

Recordá suscribirte aquí para recibir las últimas actualizaciones todas las semanas.

Referencias:
Libro Head First Design Patterns. Versión digital.

Leave a Reply

Your email address will not be published. Required fields are marked *