RMI de Java - Guía rápida

RMI significa Remote Method Invocation. Es un mecanismo que permite que un objeto que reside en un sistema (JVM) acceda / invoque un objeto que se ejecuta en otra JVM.

RMI se utiliza para crear aplicaciones distribuidas; proporciona comunicación remota entre programas Java. Se proporciona en el paquete.java.rmi.

Arquitectura de una aplicación RMI

En una aplicación RMI, escribimos dos programas, un server program (reside en el servidor) y un client program (reside en el cliente).

  • Dentro del programa del servidor, se crea un objeto remoto y la referencia de ese objeto está disponible para el cliente (usando el registro).

  • El programa cliente solicita los objetos remotos en el servidor e intenta invocar sus métodos.

El siguiente diagrama muestra la arquitectura de una aplicación RMI.

Analicemos ahora los componentes de esta arquitectura.

  • Transport Layer- Esta capa conecta al cliente y al servidor. Gestiona la conexión existente y también establece nuevas conexiones.

  • Stub- Un stub es una representación (proxy) del objeto remoto en el cliente. Reside en el sistema del cliente; actúa como puerta de enlace para el programa cliente.

  • Skeleton - Este es el objeto que reside en el lado del servidor. stub se comunica con este esqueleto para pasar la solicitud al objeto remoto.

  • RRL(Remote Reference Layer) - Es la capa que gestiona las referencias que hace el cliente al objeto remoto.

Funcionamiento de una aplicación RMI

Los siguientes puntos resumen cómo funciona una aplicación RMI:

  • Cuando el cliente realiza una llamada al objeto remoto, el stub lo recibe y finalmente pasa esta solicitud al RRL.

  • Cuando el RRL del lado del cliente recibe la solicitud, invoca un método llamado invoke() del objeto remoteRef. Pasa la solicitud al RRL en el lado del servidor.

  • El RRL en el lado del servidor pasa la solicitud al Skeleton (proxy en el servidor) que finalmente invoca el objeto requerido en el servidor.

  • El resultado se devuelve al cliente.

Marshalling y Unmarshalling

Siempre que un cliente invoca un método que acepta parámetros en un objeto remoto, los parámetros se agrupan en un mensaje antes de enviarse a través de la red. Estos parámetros pueden ser de tipo primitivo u objetos. En el caso del tipo primitivo, los parámetros se juntan y se le adjunta un encabezado. En caso de que los parámetros sean objetos, entonces se serializan. Este proceso se conoce comomarshalling.

En el lado del servidor, los parámetros empaquetados se desagregan y luego se invoca el método requerido. Este proceso se conoce comounmarshalling.

Registro RMI

El registro RMI es un espacio de nombres en el que se colocan todos los objetos del servidor. Cada vez que el servidor crea un objeto, registra este objeto con el registro RMI (usandobind() o reBind()métodos). Estos se registran con un nombre único conocido comobind name.

Para invocar un objeto remoto, el cliente necesita una referencia de ese objeto. En ese momento, el cliente obtiene el objeto del registro usando su nombre de enlace (usandolookup() método).

La siguiente ilustración explica todo el proceso:

Objetivos de RMI

Los siguientes son los objetivos de RMI:

  • Minimizar la complejidad de la aplicación.
  • Para preservar la seguridad del tipo.
  • Recolección de basura distribuida.
  • Minimice la diferencia entre trabajar con objetos locales y remotos.

Para escribir una aplicación RMI Java, deberá seguir los pasos que se indican a continuación:

  • Definir la interfaz remota
  • Desarrollar la clase de implementación (objeto remoto)
  • Desarrollar el programa del servidor
  • Desarrollar el programa del cliente
  • Compila la aplicación
  • Ejecuta la aplicación

Definición de la interfaz remota

Una interfaz remota proporciona la descripción de todos los métodos de un objeto remoto en particular. El cliente se comunica con esta interfaz remota.

Para crear una interfaz remota:

  • Cree una interfaz que amplíe la interfaz predefinida Remote que pertenece al paquete.

  • Declare todos los métodos comerciales que el cliente puede invocar en esta interfaz.

  • Dado que existe la posibilidad de problemas de red durante las llamadas remotas, una excepción denominada RemoteExceptionpuede ocurrir; tírarlo.

A continuación se muestra un ejemplo de una interfaz remota. Aquí hemos definido una interfaz con el nombreHello y tiene un método llamado printMsg().

import java.rmi.Remote; 
import java.rmi.RemoteException;  

// Creating Remote interface for our application 
public interface Hello extends Remote {  
   void printMsg() throws RemoteException;  
}

Desarrollo de la clase de implementación (objeto remoto)

Necesitamos implementar la interfaz remota creada en el paso anterior. (Podemos escribir una clase de implementación por separado o podemos hacer que el programa del servidor implemente esta interfaz directamente).

Para desarrollar una clase de implementación:

  • Implemente la interfaz creada en el paso anterior.
  • Proporcionar implementación a todos los métodos abstractos de la interfaz remota.

A continuación se muestra una clase de implementación. Aquí, hemos creado una clase llamadaImplExample e implementé la interfaz Hello creado en el paso anterior y proporcionado body para este método que imprime un mensaje.

// Implementing the remote interface 
public class ImplExample implements Hello {  
   
   // Implementing the interface method 
   public void printMsg() {  
      System.out.println("This is an example RMI program");  
   }  
}

Desarrollar el programa de servidor

Un programa de servidor RMI debe implementar la interfaz remota o extender la clase de implementación. Aquí, deberíamos crear un objeto remoto y vincularlo alRMIregistry.

Para desarrollar un programa de servidor:

  • Cree una clase de cliente desde donde desee invocar el objeto remoto.

  • Create a remote object instanciando la clase de implementación como se muestra a continuación.

  • Exportar el objeto remoto usando el método exportObject() de la clase nombrada UnicastRemoteObject que pertenece al paquete java.rmi.server.

  • Obtenga el registro RMI usando el getRegistry() método del LocateRegistry clase que pertenece al paquete java.rmi.registry.

  • Vincular el objeto remoto creado al registro usando el bind() método de la clase llamada Registry. Para este método, pase una cadena que represente el nombre de enlace y el objeto exportado, como parámetros.

A continuación se muestra un ejemplo de un programa de servidor RMI.

import java.rmi.registry.Registry; 
import java.rmi.registry.LocateRegistry; 
import java.rmi.RemoteException; 
import java.rmi.server.UnicastRemoteObject; 

public class Server extends ImplExample { 
   public Server() {} 
   public static void main(String args[]) { 
      try { 
         // Instantiating the implementation class 
         ImplExample obj = new ImplExample(); 
    
         // Exporting the object of implementation class  
         // (here we are exporting the remote object to the stub) 
         Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);  
         
         // Binding the remote object (stub) in the registry 
         Registry registry = LocateRegistry.getRegistry(); 
         
         registry.bind("Hello", stub);  
         System.err.println("Server ready"); 
      } catch (Exception e) { 
         System.err.println("Server exception: " + e.toString()); 
         e.printStackTrace(); 
      } 
   } 
}

Desarrollar el programa del cliente

Escriba un programa cliente en él, recupere el objeto remoto e invoque el método requerido usando este objeto.

Para desarrollar un programa de cliente:

  • Crea una clase de cliente desde donde pretendes invocar el objeto remoto.

  • Obtenga el registro RMI usando el getRegistry() método del LocateRegistry clase que pertenece al paquete java.rmi.registry.

  • Obtener el objeto del registro usando el método lookup() de la clase Registry que pertenece al paquete java.rmi.registry.

    Para este método, debe pasar un valor de cadena que represente el nombre de enlace como parámetro. Esto le devolverá el objeto remoto.

  • El lookup () devuelve un objeto de tipo remoto, lo convierte al tipo Hello.

  • Finalmente, invoque el método requerido utilizando el objeto remoto obtenido.

A continuación se muestra un ejemplo de un programa de cliente RMI.

import java.rmi.registry.LocateRegistry; 
import java.rmi.registry.Registry;  

public class Client {  
   private Client() {}  
   public static void main(String[] args) {  
      try {  
         // Getting the registry 
         Registry registry = LocateRegistry.getRegistry(null); 
    
         // Looking up the registry for the remote object 
         Hello stub = (Hello) registry.lookup("Hello"); 
    
         // Calling the remote method using the obtained object 
         stub.printMsg(); 
         
         // System.out.println("Remote method invoked"); 
      } catch (Exception e) {
         System.err.println("Client exception: " + e.toString()); 
         e.printStackTrace(); 
      } 
   } 
}

Compilar la aplicación

Para compilar la aplicación:

  • Compile la interfaz remota.
  • Compile la clase de implementación.
  • Compile el programa del servidor.
  • Compile el programa cliente.

O,

Abra la carpeta donde ha almacenado todos los programas y compile todos los archivos Java como se muestra a continuación.

Javac *.java

Ejecución de la aplicación

Step 1 - Inicie el rmi registro usando el siguiente comando.

start rmiregistry

Esto iniciará un rmi registro en una ventana separada como se muestra a continuación.

Step 2 - Ejecute el archivo de clase del servidor como se muestra a continuación.

Java Server

Step 3 - Ejecute el archivo de clase de cliente como se muestra a continuación.

java Client

Verification - Tan pronto como inicie el cliente, verá el siguiente resultado en el servidor.

En el capítulo anterior, creamos una aplicación RMI de muestra. En este capítulo, explicaremos cómo crear una aplicación RMI donde un cliente invoca un método que muestra una ventana GUI (JavaFX).

Definición de la interfaz remota

Aquí, estamos definiendo una interfaz remota llamada Hello con un método llamado animation() en eso.

import java.rmi.Remote; 
import java.rmi.RemoteException;  

// Creating Remote interface for our application 
public interface Hello extends Remote { 
   void animation() throws RemoteException; 
}

Desarrollar la clase de implementación

En la clase de implementación (objeto remoto) de esta aplicación, estamos intentando crear una ventana que muestre el contenido de la GUI, utilizando JavaFX.

import javafx.animation.RotateTransition;  
import javafx.application.Application;  
import javafx.event.EventHandler;   

import javafx.scene.Group;  
import javafx.scene.PerspectiveCamera;  
import javafx.scene.Scene;  
import javafx.scene.control.TextField;  
import javafx.scene.input.KeyEvent;  
import javafx.scene.paint.Color;  
import javafx.scene.paint.PhongMaterial; 
  
import javafx.scene.shape.Box;  
import javafx.scene.text.Font;  
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;   
import javafx.scene.transform.Rotate;  

import javafx.stage.Stage;  
import javafx.util.Duration;  

// Implementing the remote interface 
public class FxSample extends Application implements Hello {  
   @Override  
   public void start(Stage stage) { 
      // Drawing a Box  
      Box box = new Box();  

      // Setting the properties of the Box  
      box.setWidth(150.0);  
      box.setHeight(150.0);    
      box.setDepth(100.0);  

      // Setting the position of the box  
      box.setTranslateX(350);   
      box.setTranslateY(150);  
      box.setTranslateZ(50);  

      // Setting the text  
      Text text = new Text(
         "Type any letter to rotate the box, and click on the box to stop the rotation");

      // Setting the font of the text  
      text.setFont(Font.font(null, FontWeight.BOLD, 15));      

      // Setting the color of the text  
      text.setFill(Color.CRIMSON);  

      // Setting the position of the text  
      text.setX(20);  
      text.setY(50); 

      // Setting the material of the box  
      PhongMaterial material = new PhongMaterial();   
      material.setDiffuseColor(Color.DARKSLATEBLUE);   

      // Setting the diffuse color material to box  
      box.setMaterial(material);        

      // Setting the rotation animation to the box     
      RotateTransition rotateTransition = new RotateTransition();  

      // Setting the duration for the transition  
      rotateTransition.setDuration(Duration.millis(1000));  

      // Setting the node for the transition  
      rotateTransition.setNode(box);        

      // Setting the axis of the rotation  
      rotateTransition.setAxis(Rotate.Y_AXIS);  

      // Setting the angle of the rotation 
      rotateTransition.setByAngle(360);  

      // Setting the cycle count for the transition  
      rotateTransition.setCycleCount(50);  

      // Setting auto reverse value to false  
      rotateTransition.setAutoReverse(false);   

      // Creating a text filed  
      TextField textField = new TextField();    

      // Setting the position of the text field  
      textField.setLayoutX(50);  
      textField.setLayoutY(100);  

      // Handling the key typed event  
      EventHandler<KeyEvent> eventHandlerTextField = new EventHandler<KeyEvent>() {  
         @Override  
         public void handle(KeyEvent event) {  
            // Playing the animation  
            rotateTransition.play();  
         }            
      };               
      
      // Adding an event handler to the text feld  
      textField.addEventHandler(KeyEvent.KEY_TYPED, eventHandlerTextField);  

      // Handling the mouse clicked event(on box)  
      EventHandler<javafx.scene.input.MouseEvent> eventHandlerBox =  
         new EventHandler<javafx.scene.input.MouseEvent>() {  
         @Override  
         public void handle(javafx.scene.input.MouseEvent e) {  
            rotateTransition.stop();   
         }  
      };  
      
      // Adding the event handler to the box   
      box.addEventHandler(javafx.scene.input.MouseEvent.MOUSE_CLICKED, eventHandlerBox); 

      // Creating a Group object 
      Group root = new Group(box, textField, text);  

      // Creating a scene object  
      Scene scene = new Scene(root, 600, 300);       

      // Setting camera  
      PerspectiveCamera camera = new PerspectiveCamera(false);  
      camera.setTranslateX(0);  
      camera.setTranslateY(0);  
      camera.setTranslateZ(0);  
      scene.setCamera(camera);   

      // Setting title to the Stage
      stage.setTitle("Event Handlers Example");  

      // Adding scene to the stage  
      stage.setScene(scene);  

      // Displaying the contents of the stage  
      stage.show();  
   }  

   // Implementing the interface method 
   public void animation() {  
      launch();  
   }  
}

Programa de servidor

Un programa de servidor RMI debe implementar la interfaz remota o extender la clase de implementación. Aquí, deberíamos crear un objeto remoto y vincularlo alRMIregistry.

A continuación se muestra el programa de servidor de esta aplicación. Aquí, ampliaremos la clase creada anteriormente, crearemos un objeto remoto y lo registraremos en el registro RMI con el nombre de enlace.hello.

import java.rmi.registry.Registry; 
import java.rmi.registry.LocateRegistry; 
import java.rmi.RemoteException; 
import java.rmi.server.UnicastRemoteObject; 

public class Server extends FxSample { 
   public Server() {} 
   public static void main(String args[]) { 
      try { 
         // Instantiating the implementation class 
         FxSample obj = new FxSample();
      
         // Exporting the object of implementation class  
         // (here we are exporting the remote object to the stub) 
         Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);  
      
         // Binding the remote object (stub) in the registry 
         Registry registry = LocateRegistry.getRegistry(); 
         
         registry.bind("Hello", stub);  
         System.err.println("Server ready"); 
      } catch (Exception e) { 
         System.err.println("Server exception: " + e.toString()); 
         e.printStackTrace(); 
      } 
   } 
}

Programa de cliente

A continuación se muestra el programa cliente de esta aplicación. Aquí, buscamos el objeto remoto e invocamos su método llamadoanimation().

import java.rmi.registry.LocateRegistry; 
import java.rmi.registry.Registry;  

public class Client { 
   private Client() {} 
   public static void main(String[] args) {  
      try { 
         // Getting the registry 
         Registry registry = LocateRegistry.getRegistry(null); 
    
         // Looking up the registry for the remote object 
         Hello stub = (Hello) registry.lookup("Hello"); 
         
         // Calling the remote method using the obtained object 
         stub.animation(); 
         
         System.out.println("Remote method invoked"); 
      } catch (Exception e) {
         System.err.println("Client exception: " + e.toString()); 
         e.printStackTrace(); 
      } 
   } 
}

Pasos para ejecutar el ejemplo

Los siguientes son los pasos para ejecutar nuestro ejemplo RMI.

Step 1 - Abra la carpeta donde ha almacenado todos los programas y compile todos los archivos Java como se muestra a continuación.

Javac *.java

Step 2 - Inicie el rmi registro usando el siguiente comando.

start rmiregistry

Esto iniciará un rmi registro en una ventana separada como se muestra a continuación.

Step 3 - Ejecute el archivo de clase del servidor como se muestra a continuación.

Java Server

Step 4 - Ejecute el archivo de clase de cliente como se muestra a continuación.

java Client

Verification - Tan pronto como inicie el cliente, verá el siguiente resultado en el servidor.

En el capítulo anterior, creamos una aplicación RMI de muestra donde un cliente invoca un método que muestra una ventana GUI (JavaFX).

En este capítulo, tomaremos un ejemplo para ver cómo un programa cliente puede recuperar los registros de una tabla en la base de datos MySQL que reside en el servidor.

Supongamos que tenemos una tabla llamada student_data en la base de datos details Como se muestra abajo.

+----+--------+--------+------------+---------------------+ 
| ID | NAME   | BRANCH | PERCENTAGE | EMAIL               | 
+----+--------+--------+------------+---------------------+ 
|  1 | Ram    | IT     |         85 | [email protected]    | 
|  2 | Rahim  | EEE    |         95 | [email protected]  | 
|  3 | Robert | ECE    |         90 | [email protected] | 
+----+--------+--------+------------+---------------------+

Suponga que el nombre del usuario es myuser y su contraseña es password.

Crear una clase para estudiantes

Crear un Student clase con setter y getter métodos como se muestra a continuación.

public class Student implements java.io.Serializable {   
   private int id, percent;   
   private String name, branch, email;    
  
   public int getId() { 
      return id; 
   } 
   public String getName() { 
      return name; 
   } 
   public String getBranch() { 
      return branch; 
   } 
   public int getPercent() { 
      return percent; 
   } 
   public String getEmail() { 
      return email; 
   } 
   public void setID(int id) { 
      this.id = id; 
   } 
   public void setName(String name) { 
      this.name = name; 
   } 
   public void setBranch(String branch) { 
      this.branch = branch; 
   } 
   public void setPercent(int percent) { 
      this.percent = percent; 
   } 
   public void setEmail(String email) { 
      this.email = email; 
   } 
}

Definición de la interfaz remota

Defina la interfaz remota. Aquí, estamos definiendo una interfaz remota llamadaHello con un método llamado getStudents ()en eso. Este método devuelve una lista que contiene el objeto de la clase.Student.

import java.rmi.Remote; 
import java.rmi.RemoteException; 
import java.util.*;

// Creating Remote interface for our application 
public interface Hello extends Remote {  
   public List<Student> getStudents() throws Exception;  
}

Desarrollar la clase de implementación

Cree una clase e implemente lo creado anteriormente interface.

Aquí estamos implementando el getStudents() método del Remote interface. Cuando invoca este método, recupera los registros de una tabla llamadastudent_data. Establece estos valores en la clase Student usando sus métodos de establecimiento, los agrega a un objeto de lista y devuelve esa lista.

import java.sql.*; 
import java.util.*;  

// Implementing the remote interface 
public class ImplExample implements Hello {  
   
   // Implementing the interface method 
   public List<Student> getStudents() throws Exception {  
      List<Student> list = new ArrayList<Student>();   
    
      // JDBC driver name and database URL 
      String JDBC_DRIVER = "com.mysql.jdbc.Driver";   
      String DB_URL = "jdbc:mysql://localhost:3306/details";  
      
      // Database credentials 
      String USER = "myuser"; 
      String PASS = "password";  
      
      Connection conn = null; 
      Statement stmt = null;  
      
      //Register JDBC driver 
      Class.forName("com.mysql.jdbc.Driver");   
      
      //Open a connection
      System.out.println("Connecting to a selected database..."); 
      conn = DriverManager.getConnection(DB_URL, USER, PASS); 
      System.out.println("Connected database successfully...");  
      
      //Execute a query 
      System.out.println("Creating statement..."); 
      
      stmt = conn.createStatement();  
      String sql = "SELECT * FROM student_data"; 
      ResultSet rs = stmt.executeQuery(sql);  
      
      //Extract data from result set 
      while(rs.next()) { 
         // Retrieve by column name 
         int id  = rs.getInt("id"); 
         
         String name = rs.getString("name"); 
         String branch = rs.getString("branch"); 
         
         int percent = rs.getInt("percentage"); 
         String email = rs.getString("email");  
         
         // Setting the values 
         Student student = new Student(); 
         student.setID(id); 
         student.setName(name); 
         student.setBranch(branch); 
         student.setPercent(percent); 
         student.setEmail(email); 
         list.add(student); 
      } 
      rs.close(); 
      return list;     
   }  
}

Programa de servidor

Un programa de servidor RMI debe implementar la interfaz remota o extender la clase de implementación. Aquí, deberíamos crear un objeto remoto y vincularlo alRMI registry.

A continuación se muestra el programa de servidor de esta aplicación. Aquí, ampliaremos la clase creada anteriormente, crearemos un objeto remoto y lo registraremos en el registro RMI con el nombre de enlace.hello.

import java.rmi.registry.Registry; 
import java.rmi.registry.LocateRegistry; 
import java.rmi.RemoteException; 
import java.rmi.server.UnicastRemoteObject; 

public class Server extends ImplExample { 
   public Server() {} 
   public static void main(String args[]) { 
      try { 
         // Instantiating the implementation class 
         ImplExample obj = new ImplExample(); 
    
         // Exporting the object of implementation class (
            here we are exporting the remote object to the stub) 
         Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);  
         
         // Binding the remote object (stub) in the registry 
         Registry registry = LocateRegistry.getRegistry(); 
         
         registry.bind("Hello", stub);  
         System.err.println("Server ready"); 
      } catch (Exception e) { 
         System.err.println("Server exception: " + e.toString()); 
         e.printStackTrace(); 
      } 
   } 
}

Programa de cliente

A continuación se muestra el programa cliente de esta aplicación. Aquí, buscamos el objeto remoto e invocamos el método llamadogetStudents(). Recupera los registros de la tabla del objeto de lista y los muestra.

import java.rmi.registry.LocateRegistry; 
import java.rmi.registry.Registry; 
import java.util.*;  

public class Client {  
   private Client() {}  
   public static void main(String[] args)throws Exception {  
      try { 
         // Getting the registry 
         Registry registry = LocateRegistry.getRegistry(null); 
    
         // Looking up the registry for the remote object 
         Hello stub = (Hello) registry.lookup("Hello"); 
    
         // Calling the remote method using the obtained object 
         List<Student> list = (List)stub.getStudents(); 
         for (Student s:list)v { 
            
            // System.out.println("bc "+s.getBranch()); 
            System.out.println("ID: " + s.getId()); 
            System.out.println("name: " + s.getName()); 
            System.out.println("branch: " + s.getBranch()); 
            System.out.println("percent: " + s.getPercent()); 
            System.out.println("email: " + s.getEmail()); 
         }  
      // System.out.println(list); 
      } catch (Exception e) { 
         System.err.println("Client exception: " + e.toString()); 
         e.printStackTrace(); 
      } 
   } 
}

Pasos para ejecutar el ejemplo

Los siguientes son los pasos para ejecutar nuestro ejemplo RMI.

Step 1 - Abra la carpeta donde ha almacenado todos los programas y compile todos los archivos Java como se muestra a continuación.

Javac *.java

Step 2 - Inicie el rmi registro usando el siguiente comando.

start rmiregistry

Esto iniciará un rmi registro en una ventana separada como se muestra a continuación.

Step 3 - Ejecute el archivo de clase del servidor como se muestra a continuación.

Java Server

Step 4 - Ejecute el archivo de clase de cliente como se muestra a continuación.

java Client