Salut! Aujourd'hui, nous allons aborder un sujet plutôt intéressant : Java RMI. Cela signifie Remote Method Invocation. Vous pouvez utiliser RMI pour permettre à deux programmes de communiquer entre eux, même s'ils se trouvent sur des ordinateurs différents. Ça a l'air cool ? :) Et ce n'est pas si difficile à faire ! Dans la leçon d'aujourd'hui, nous allons analyser les éléments de l'interaction RMI et découvrir comment la configurer. La première chose dont nous avons besoin est un client et un serveur. Nous n'avons pas vraiment besoin d'approfondir la terminologie informatique. En ce qui concerne le RMI, ce ne sont que deux programmes. L'un d'eux inclura un objet et l'autre appellera des méthodes sur cet objet. Appeler des méthodes d'un objet qui existe dans un programme différent — c'est quelque chose que nous n'avons pas encore fait ! Il est temps d'essayer ! :) Pour éviter de s'enliser, laissez' s garder notre programme simple. En général, un serveur effectue certains calculs demandés par un client. Et il en sera ainsi avec nous. Notre serveur sera un simple programme de calculatrice. Il n'aura qu'une seule méthode :multiplier() . Il multipliera deux nombres qui lui seront envoyés par le programme client, puis renverra le résultat. Tout d'abord, nous avons besoin d'une interface :

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

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Pourquoi avons-nous besoin d'une interface ? Parce que RMI s'appuie sur la création de proxys, que vous avez étudiés dans les leçons précédentes . Comme vous vous en souvenez probablement, nous travaillons avec des proxys via des interfaces, pas des classes. Il y a 2 exigences importantes pour notre interface !
  1. Il doit étendre l'interface Remote.
  2. Toutes ses méthodes doivent lever une RemoteException (l'EDI ne le fera pas automatiquement — vous devez l'ajouter manuellement !).
Nous devons maintenant créer une classe de serveur qui implémente notre interface Calculatrice . L'IRM en pratique - 2Ici aussi, tout est assez simple :

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

   @Override
   public int multiply(int x, int y) throws RemoteException {
       return x*y;
   }

}
Il n'y a vraiment rien à commenter ici :) Maintenant, nous devons écrire un programme serveur qui configurera et exécutera notre objet calculatrice. Il ressemblera à ceci:

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

public class ServerMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {

       final RemoteCalculationServer server = new RemoteCalculationServer();

       final Registry registry = LocateRegistry.createRegistry(2732);

       Remote stub = UnicastRemoteObject.exportObject(server, 0);
       registry.bind(UNIQUE_BINDING_NAME, stub);

       Thread.sleep(Integer.MAX_VALUE);

   }
}
Comprenons cela :) Dans la première ligne, nous déclarons une variable String :

public static final String UNIQUE_BINDING_NAME = "server.calculator";
Cette chaîne est le nom unique de l'objet distant. Notre programme client utilise ce nom pour trouver notre serveur : vous le verrez plus tard. Ensuite, nous créons notre objet calculateur :

final RemoteCalculationServer server = new RemoteCalculationServer();
Tout est clair ici. La suite est plus intéressante :

final Registry registry = LocateRegistry.createRegistry(2732);
Cet objet Registry est un registre d'objets distants. Ce sont des objets auxquels d'autres programmes peuvent accéder à distance :) Nous avons passé le numéro 2732 à la méthode LocateRegistry.createRegistry() . C'est le numéro de port - un numéro unique que d'autres programmes utiliseront pour trouver notre registre d'objets (encore une fois, vous le verrez ci-dessous). Continuons... Voyons ce qui se passe dans la ligne suivante :

Remote stub = UnicastRemoteObject.exportObject(server, 0);
Nous créons un stub dans cette ligne. Un stub encapsule l'intégralité de l'appel distant. Vous pouvez considérer cela comme l'élément le plus important du RMI. Qu'est ce que ça fait?
  1. Il reçoit toutes les informations sur un appel distant d'une méthode.
  2. Si la méthode a des paramètres, le stub les désérialisera. Faites attention à ce point ! Les arguments que vous passez aux méthodes appelées à distance doivent être sérialisables (après tout, ils seront transmis sur le réseau). Ce n'est pas un problème pour nous — nous ne faisons que transmettre des numéros. Mais si vous transmettez des objets, n'oubliez pas cette exigence !
  3. Après cela, le stub appelle la méthode souhaitée.
Nous passons notre objet serveur calculateur à la méthode UnicastRemoteObject.exportObject() . C'est ainsi que nous rendons possible l'appel à distance de ses méthodes. Il ne reste plus qu'une chose à faire :

registry.bind(UNIQUE_BINDING_NAME, stub);
Nous "enregistrons" notre stub dans le registre d'objets distants sous le nom que nous avons créé au tout début. Maintenant, le client pourra le trouver ! Peut-être avez-vous remarqué que nous avons mis le thread principal du programme en veille à la fin :

Thread.sleep(Integer.MAX_VALUE);
Nous avons juste besoin que le serveur fonctionne pendant longtemps. Dans l'EDI, nous lancerons simultanément deux méthodes main() : premièrement, la méthode main() du serveur (dans la classe ServerMain , que nous avons déjà écrite), et deuxièmement, la méthode main() du client (dans la classe ClientMain , que nous écrirons ci-dessous). Il est important que le programme serveur ne se termine pas pendant que nous démarrons le client, nous le mettons donc en veille pendant une longue période. Dans tous les cas, il continuera à fonctionner :) Nous pouvons maintenant exécuter la méthode main() de notre serveur . Laissez-le s'exécuter et attendez que le programme client appelle une méthode :) Écrivons maintenant le programme client ! Il enverra des nombres à notre serveur pour multiplication.

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

public class ClientMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, NotBoundException {

       final Registry registry = LocateRegistry.getRegistry(2732);

       Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);

       int multiplyResult = calculator.multiply(20, 30);

       System.out.println(multiplyResult);
   }
}
Cela semble simple. Mais que se passe-t-il ici ? Premièrement, le client doit connaître le nom unique de l'objet dont il appellera les méthodes à distance. En conséquence, dans le programme client, nous avons créé la chaîne finale statique publique UNIQUE_BINDING_NAME = "server.calculator" ; variable. Ensuite, dans la méthode main() , nous avons accès au registre des objets distants. Pour ce faire, nous devons appeler la méthode LocateRegistry.getRegistry() et transmettre le numéro de port utilisé pour créer notre registre dans le programme ServerMain (port 2732 ; ce numéro est juste un exemple — vous pouvez essayer d'utiliser un numéro différent) :

final Registry registry = LocateRegistry.getRegistry(2732);
Il ne nous reste plus qu'à récupérer l'objet souhaité dans le registre ! C'est facile, car nous connaissons son nom unique !

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Faites attention au casting de type. Nous transtypons l'objet reçu dans l' interface Calculator , pas dans la classe RemoteCalculationServer . Comme nous l'avons dit au début de la leçon, RMI s'appuie sur un proxy, donc les appels distants ne sont disponibles que pour les méthodes d'une interface, pas pour les méthodes d'une classe. Enfin, nous appelons à distance la méthode multiplier () sur notre objet et envoyons le résultat à la console.

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
La méthode main() de la classe ServerMain est déjà en cours d'exécution depuis longtemps. Il est maintenant temps d'exécuter la méthode main() dans le programme client ( ClientMain ) ! Sortie console :

600
C'est ça! Notre programme (deux programmes, en fait !) a fait ce qu'il était censé faire :) Si vous avez le temps et l'envie, vous pouvez pimenter un peu cela. Par exemple, faites en sorte que la calculatrice prenne en charge les quatre opérations arithmétiques standard et transmettez les nombres non pas en tant que types primitifs, mais en tant qu'objets CalculationInstance(int x, int y) . Rendez-vous dans la prochaine leçon! :)