Java RMI - Guida rapida

RMI sta per Remote Method Invocation. È un meccanismo che consente a un oggetto che risiede in un sistema (JVM) di accedere / richiamare un oggetto in esecuzione su un'altra JVM.

RMI viene utilizzato per creare applicazioni distribuite; fornisce la comunicazione remota tra i programmi Java. Viene fornito nella confezionejava.rmi.

Architettura di un'applicazione RMI

In un'applicazione RMI, scriviamo due programmi, a server program (risiede sul server) e un file client program (risiede nel cliente).

  • All'interno del programma server, viene creato un oggetto remoto e il riferimento a tale oggetto viene reso disponibile per il client (utilizzando il registro).

  • Il programma client richiede gli oggetti remoti sul server e tenta di richiamare i suoi metodi.

Il diagramma seguente mostra l'architettura di un'applicazione RMI.

Parliamo ora dei componenti di questa architettura.

  • Transport Layer- Questo livello collega il client e il server. Gestisce la connessione esistente e crea anche nuove connessioni.

  • Stub- Uno stub è una rappresentazione (proxy) dell'oggetto remoto sul client. Risiede nel sistema client; funge da gateway per il programma client.

  • Skeleton - Questo è l'oggetto che risiede sul lato server. stub comunica con questo scheletro per passare la richiesta all'oggetto remoto.

  • RRL(Remote Reference Layer) - È il layer che gestisce i riferimenti fatti dal client all'oggetto remoto.

Lavorare su un'applicazione RMI

I seguenti punti riassumono il funzionamento di un'applicazione RMI:

  • Quando il client effettua una chiamata all'oggetto remoto, viene ricevuta dallo stub che alla fine passa questa richiesta all'RRL.

  • Quando l'RRL lato client riceve la richiesta, richiama un metodo chiamato invoke() dell'oggetto remoteRef. Passa la richiesta all'RRL sul lato server.

  • L'RRL sul lato server passa la richiesta allo Skeleton (proxy sul server) che infine richiama l'oggetto richiesto sul server.

  • Il risultato viene passato di nuovo al client.

Marshalling e Unmarshalling

Ogni volta che un client richiama un metodo che accetta parametri su un oggetto remoto, i parametri vengono raggruppati in un messaggio prima di essere inviati sulla rete. Questi parametri possono essere di tipo primitivo o oggetti. In caso di tipo primitivo, i parametri vengono messi insieme e ad esso viene allegata un'intestazione. Nel caso in cui i parametri siano oggetti, vengono serializzati. Questo processo è noto comemarshalling.

Sul lato server, i parametri compressi vengono disaggregati e quindi viene richiamato il metodo richiesto. Questo processo è noto comeunmarshalling.

Registro RMI

Il registro RMI è uno spazio dei nomi in cui vengono posizionati tutti gli oggetti del server. Ogni volta che il server crea un oggetto, registra questo oggetto con RMIregistry (utilizzandobind() o reBind()metodi). Questi vengono registrati utilizzando un nome univoco noto comebind name.

Per richiamare un oggetto remoto, il client necessita di un riferimento a tale oggetto. A quel punto, il client recupera l'oggetto dal registro utilizzando il suo nome di bind (utilizzandolookup() metodo).

La seguente illustrazione spiega l'intero processo:

Obiettivi di RMI

Di seguito sono riportati gli obiettivi di RMI:

  • Per ridurre al minimo la complessità dell'applicazione.
  • Per preservare l'indipendenza dai tipi.
  • Garbage collection distribuita.
  • Ridurre al minimo la differenza tra lavorare con oggetti locali e remoti.

Per scrivere un'applicazione Java RMI, è necessario seguire i passaggi indicati di seguito:

  • Definisci l'interfaccia remota
  • Sviluppa la classe di implementazione (oggetto remoto)
  • Sviluppa il programma server
  • Sviluppa il programma client
  • Compilare l'applicazione
  • Esegui l'applicazione

Definizione dell'interfaccia remota

Un'interfaccia remota fornisce la descrizione di tutti i metodi di un particolare oggetto remoto. Il client comunica con questa interfaccia remota.

Per creare un'interfaccia remota:

  • Crea un'interfaccia che estenda l'interfaccia predefinita Remote che appartiene al pacchetto.

  • Dichiarare tutti i metodi di business che possono essere richiamati dal client in questa interfaccia.

  • Poiché esiste la possibilità di problemi di rete durante le chiamate remote, un'eccezione denominata RemoteExceptionpotrebbe capitare; gettarlo.

Di seguito è riportato un esempio di un'interfaccia remota. Qui abbiamo definito un'interfaccia con il nomeHello e ha un metodo chiamato printMsg().

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

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

Sviluppo della classe di implementazione (oggetto remoto)

Dobbiamo implementare l'interfaccia remota creata nel passaggio precedente. (Possiamo scrivere una classe di implementazione separatamente o possiamo fare in modo che il programma server implementi direttamente questa interfaccia.)

Per sviluppare una classe di implementazione -

  • Implementa l'interfaccia creata nel passaggio precedente.
  • Fornire implementazione a tutti i metodi astratti dell'interfaccia remota.

Di seguito è una classe di implementazione. Qui abbiamo creato una classe denominataImplExample e implementato l'interfaccia Hello creato nel passaggio precedente e fornito body per questo metodo che stampa un messaggio.

// 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");  
   }  
}

Sviluppo del programma server

Un programma server RMI dovrebbe implementare l'interfaccia remota o estendere la classe di implementazione. Qui, dovremmo creare un oggetto remoto e associarlo al fileRMIregistry.

Per sviluppare un programma server:

  • Crea una classe client da cui desideri richiamare l'oggetto remoto.

  • Create a remote object istanziando la classe di implementazione come mostrato di seguito.

  • Esporta l'oggetto remoto utilizzando il metodo exportObject() della classe denominata UnicastRemoteObject che appartiene al pacchetto java.rmi.server.

  • Ottieni il registro RMI utilizzando il getRegistry() metodo del LocateRegistry classe che appartiene al pacchetto java.rmi.registry.

  • Associare l'oggetto remoto creato al registro utilizzando l'estensione bind() metodo della classe denominata Registry. A questo metodo, passare una stringa che rappresenta il nome del bind e l'oggetto esportato, come parametri.

Di seguito è riportato un esempio di un programma server 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(); 
      } 
   } 
}

Sviluppo del programma client

Scrivici un programma client, prendi l'oggetto remoto e invoca il metodo richiesto usando questo oggetto.

Per sviluppare un programma client -

  • Crea una classe client da cui intendi richiamare l'oggetto remoto.

  • Ottieni il registro RMI utilizzando il getRegistry() metodo del LocateRegistry classe che appartiene al pacchetto java.rmi.registry.

  • Recupera l'oggetto dal registro utilizzando il metodo lookup() della classe Registry che appartiene al pacchetto java.rmi.registry.

    A questo metodo, è necessario passare un valore stringa che rappresenti il ​​nome del bind come parametro. Questo ti restituirà l'oggetto remoto.

  • Lookup () restituisce un oggetto di tipo remote, eseguendo il down cast al tipo Hello.

  • Infine invoca il metodo richiesto utilizzando l'oggetto remoto ottenuto.

Di seguito è riportato un esempio di un programma client 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(); 
      } 
   } 
}

Compilazione dell'applicazione

Per compilare l'applicazione:

  • Compila l'interfaccia remota.
  • Compila la classe di implementazione.
  • Compila il programma del server.
  • Compila il programma client.

O,

Apri la cartella in cui hai archiviato tutti i programmi e compila tutti i file Java come mostrato di seguito.

Javac *.java

Esecuzione dell'applicazione

Step 1 - Avvia il file rmi registro utilizzando il seguente comando.

start rmiregistry

Questo avvierà un file rmi registro su una finestra separata come mostrato di seguito.

Step 2 - Eseguire il file di classe del server come mostrato di seguito.

Java Server

Step 3 - Esegui il file della classe client come mostrato di seguito.

java Client

Verification - Non appena avvii il client, vedrai il seguente output nel server.

Nel capitolo precedente, abbiamo creato un'applicazione RMI di esempio. In questo capitolo, spiegheremo come creare un'applicazione RMI in cui un client richiama un metodo che visualizza una finestra GUI (JavaFX).

Definizione dell'interfaccia remota

Qui stiamo definendo un'interfaccia remota denominata Hello con un metodo denominato animation() dentro.

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

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

Sviluppo della classe di implementazione

Nella classe Implementation (Remote Object) di questa applicazione, stiamo cercando di creare una finestra che visualizzi il contenuto della GUI, utilizzando 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();  
   }  
}

Programma server

Un programma server RMI dovrebbe implementare l'interfaccia remota o estendere la classe di implementazione. Qui, dovremmo creare un oggetto remoto e associarlo al fileRMIregistry.

Di seguito è riportato il programma server di questa applicazione. Qui, estenderemo la classe creata sopra, creeremo un oggetto remoto e lo registreremo nel registro RMI con il nome di bindhello.

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(); 
      } 
   } 
}

Programma client

Di seguito è riportato il programma client di questa applicazione. Qui stiamo recuperando l'oggetto remoto e invocando il suo metodo denominatoanimation().

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(); 
      } 
   } 
}

Passaggi per eseguire l'esempio

Di seguito sono riportati i passaggi per eseguire il nostro esempio RMI.

Step 1 - Apri la cartella in cui hai memorizzato tutti i programmi e compila tutti i file Java come mostrato di seguito.

Javac *.java

Step 2 - Avvia il file rmi registro utilizzando il seguente comando.

start rmiregistry

Questo avvierà un file rmi registro su una finestra separata come mostrato di seguito.

Step 3 - Eseguire il file di classe del server come mostrato di seguito.

Java Server

Step 4 - Esegui il file della classe client come mostrato di seguito.

java Client

Verification - Non appena avvii il client, vedrai il seguente output nel server.

Nel capitolo precedente, abbiamo creato un'applicazione RMI di esempio in cui un client richiama un metodo che visualizza una finestra GUI (JavaFX).

In questo capitolo, faremo un esempio per vedere come un programma client può recuperare i record di una tabella nel database MySQL che risiede sul server.

Supponiamo di avere una tabella denominata student_data nel database details come mostrato di seguito.

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

Supponiamo che il nome dell'utente sia myuser e la sua password è password.

Creazione di una classe per studenti

Creare un Student classe con setter e getter metodi come mostrato di seguito.

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; 
   } 
}

Definizione dell'interfaccia remota

Definisci l'interfaccia remota. Qui stiamo definendo un'interfaccia remota denominataHello con un metodo denominato getStudents ()dentro. Questo metodo restituisce una lista che contiene l'oggetto della classeStudent.

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;  
}

Sviluppo della classe di implementazione

Crea una classe e implementa quanto sopra creato interface.

Qui stiamo implementando il getStudents() metodo del Remote interface. Quando si richiama questo metodo, vengono recuperati i record di una tabella denominatastudent_data. Imposta questi valori alla classe Student utilizzando i suoi metodi setter, lo aggiunge a un oggetto elenco e restituisce tale elenco.

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;     
   }  
}

Programma server

Un programma server RMI dovrebbe implementare l'interfaccia remota o estendere la classe di implementazione. Qui, dovremmo creare un oggetto remoto e associarlo al fileRMI registry.

Di seguito è riportato il programma server di questa applicazione. Qui, estenderemo la classe creata sopra, creeremo un oggetto remoto e lo registreremo nel registro RMI con il nome di bindhello.

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(); 
      } 
   } 
}

Programma client

Di seguito è riportato il programma client di questa applicazione. Qui stiamo recuperando l'oggetto remoto e invocando il metodo denominatogetStudents(). Recupera i record della tabella dall'oggetto elenco e li visualizza.

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(); 
      } 
   } 
}

Passaggi per eseguire l'esempio

Di seguito sono riportati i passaggi per eseguire il nostro esempio RMI.

Step 1 - Apri la cartella in cui hai memorizzato tutti i programmi e compila tutti i file Java come mostrato di seguito.

Javac *.java

Step 2 - Avvia il file rmi registro utilizzando il seguente comando.

start rmiregistry

Questo avvierà un file rmi registro su una finestra separata come mostrato di seguito.

Step 3 - Eseguire il file di classe del server come mostrato di seguito.

Java Server

Step 4 - Esegui il file della classe client come mostrato di seguito.

java Client