Exercice d’application en Java RMI
Question 1 - Définition des interfaces RMI Pour qu'une méthode Java soit accessible à distance via RMI (Remote Method Invocation), elle doit être déclarée dans une interface qui hérite de java.rmi.Remote . De plus, chaque méthode de cette interface doit déclarer qu'elle peut lever une exception de type java.rmi.RemoteException , afin de gérer les éventuelles erreurs réseau.
D'après le document Exercice d’application en Java RMI
Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Document source
Programming, Java RMI · PDF · 7 pages · 2001
Afficher l'aperçu du document
Question 1 - Définition des interfaces RMI
Pour qu'une méthode Java soit accessible à distance via RMI (Remote Method Invocation), elle doit être déclarée dans une interface qui hérite de java.rmi.Remote. De plus, chaque méthode de cette interface doit déclarer qu'elle peut lever une exception de type java.rmi.RemoteException, afin de gérer les éventuelles erreurs réseau.
Puisque chaque méthode appartient à un objet distinct, nous devons définir deux interfaces.
Note : Dans le sujet original, le nom de la méthode varie entre NbOccurrences et NbOccurrence. Nous utiliserons NbOccurrences pour conserver une cohérence sur l'ensemble du code.
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ObjetStringInterface extends Remote {
public int NbOccurrences(String c, String mot) throws RemoteException;
}
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ObjetCalculInterface extends Remote {
public int add(int a, int b) throws RemoteException;
}
Question 2 - Modification des méthodes pour l'appel à distance
Les méthodes doivent désormais signaler qu'elles peuvent renvoyer une RemoteException.
Correction du code source : Le code fourni dans l'énoncé pour NbOccurrences tente d'utiliser une variable ChaineOrigine qui n'est pas passée en paramètre, tout en ignorant le paramètre mot. Le code ci-dessous corrige cette erreur en utilisant systématiquement le paramètre mot.
public int add(int a, int b) throws RemoteException {
return a + b;
}
public int NbOccurrences(String c, String mot) throws RemoteException {
int longueur = mot.length();
int Nb = 0;
for (int i = 0; i < longueur; i++) {
if ((mot.substring(i, i + 1)).equals(c)) {
Nb++;
}
}
return Nb;
}
Question 3 - Structure des classes d'implémentation
Pour que les objets soient accessibles à distance, leurs classes doivent hériter de java.rmi.server.UnicastRemoteObject et implémenter les interfaces distantes définies à la question 1. Le constructeur de ces classes doit également lever une RemoteException.
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class ObjetString extends UnicastRemoteObject implements ObjetStringInterface {
public ObjetString() throws RemoteException {
super();
}
public int NbOccurrences(String c, String mot) throws RemoteException {
int longueur = mot.length();
int Nb = 0;
for (int i = 0; i < longueur; i++) {
if ((mot.substring(i, i + 1)).equals(c)) {
Nb++;
}
}
return Nb;
}
}
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class ObjetCalcul extends UnicastRemoteObject implements ObjetCalculInterface {
public ObjetCalcul() throws RemoteException {
super();
}
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
Question 4 - Implantation de la classe Serveur
Sur la machine clementine.cnam.fr, en plus des interfaces et des classes d'implémentation, il faut un programme principal (le serveur) chargé d'instancier les objets distants et de les enregistrer dans l'annuaire RMI (le registry). L'énoncé place l'enregistrement sur le port par défaut (1099) à ce stade.
import java.rmi.Naming;
public class Serveur {
public static void main(String[] args) {
try {
System.out.println("Serveur : Construction de l'implémentation");
ObjetString rev = new ObjetString();
System.out.println("ObjetString prêt à être lié");
Naming.rebind("rmi://clementine.cnam.fr:1099/Mot", rev);
ObjetCalcul r = new ObjetCalcul();
System.out.println("ObjetCalcul prêt à être lié");
Naming.rebind("rmi://clementine.cnam.fr:1099/Addition", r);
System.out.println("Attente des invocations des clients...");
} catch (Exception e) {
System.out.println("Erreur de liaison de l'objet ObjetCalcul ou ObjetString");
e.printStackTrace();
}
}
}
Question 5 - Déploiement avec le service de noms sur le port 2001
Si le service de noms (rmiregistry) écoute sur le port 2001, le code du serveur doit être modifié pour préciser ce port dans l'URL d'enregistrement :
Naming.rebind("rmi://clementine.cnam.fr:2001/Mot", rev);
Naming.rebind("rmi://clementine.cnam.fr:2001/Addition", r);
Commandes de déploiement à exécuter sur la machine clementine
-
Compilation des sources :
javac *.java
Génération des stubs (talons) :
(Note : L'outil rmic était requis dans les anciennes versions de Java pour générer les classes _Stub. Cette étape est présentée telle qu'attendue par le sujet, bien qu'elle soit obsolète depuis Java 5).
rmic -v1.2 ObjetCalcul
rmic -v1.2 ObjetString
Création du fichier de politique de sécurité (fichier.policy) :
Il faut créer un fichier texte nommé fichier.policy contenant les autorisations :
grant {
permission java.security.AllPermission;
};
Lancement du service de noms (rmiregistry) : Il faut spécifier le port 2001 et le fichier de politique de sécurité.
rmiregistry -J-Djava.security.policy=fichier.policy 2001 &
Lancement du serveur :
java Serveur &
Question 6 - Programme du client
Le client s'exécute sur une autre machine. Il interroge le rmiregistry de clementine pour récupérer les références des objets distants, puis appelle leurs méthodes.
Correction du code source : Le code extrait du sujet effectuait le Naming.lookup de l'addition à l'intérieur de la boucle while, ce qui provoquait une requête réseau inutile à chaque itération. Dans cette solution, les deux références (Mot et Addition) sont récupérées avant la boucle.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
public class Client {
public static void main(String[] argv) throws IOException {
int somme = 0, Nb = 0;
String ligne;
StringTokenizer st;
String c, mot;
// Mise en place du gestionnaire de sécurité
System.setSecurityManager(new RMISecurityManager());
BufferedReader entree = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Entrez la lettre à chercher, suivie de la phrase :");
ligne = entree.readLine();
st = new StringTokenizer(ligne);
if (!st.hasMoreTokens()) return;
c = st.nextToken();
if (st.hasMoreTokens()) {
mot = st.nextToken();
} else {
return; // Pas de phrase fournie
}
try {
// Récupération des références distantes (sur le port 2001)
ObjetStringInterface s = (ObjetStringInterface) Naming.lookup("rmi://clementine.cnam.fr:2001/Mot");
ObjetCalculInterface a = (ObjetCalculInterface) Naming.lookup("rmi://clementine.cnam.fr:2001/Addition");
// Traitement du premier mot
somme = s.NbOccurrences(c, mot);
// Traitement des mots suivants
while (st.hasMoreTokens()) {
mot = st.nextToken();
Nb = s.NbOccurrences(c, mot);
somme = a.add(Nb, somme);
}
System.out.println("Dans la phrase /" + ligne + "/, il y a " + somme + " occurrences de " + c);
} catch (Exception e) {
System.out.println("Erreur d'accès à un objet distant");
e.printStackTrace();
}
}
}
Lancement du client
Le répertoire du client doit contenir les interfaces (ObjetStringInterface.class, ObjetCalculInterface.class), les stubs si utilisés (ObjetString_Stub.class, ObjetCalcul_Stub.class), le fichier source Client.java et le fichier.policy.
javac Client.java
java -Djava.security.policy=fichier.policy Client
Méthode
Pour aborder un exercice de conception d'application avec Java RMI, la démarche à suivre est toujours séquentielle et obéit à des règles strictes de typage :
- Les interfaces : Elles constituent le contrat. Assurez-vous qu'elles étendent toujours
java.rmi.Remoteet que toutes les méthodes déclarentthrows java.rmi.RemoteException. - L'implémentation : Elle contient la logique métier. En héritant de
UnicastRemoteObject, l'objet devient capable d'écouter les requêtes réseau. N'oubliez jamais d'ajouter un constructeur public qui propageRemoteException. - L'enregistrement (Serveur) : C'est la phase de publication. Vérifiez toujours la syntaxe de l'URL
Naming.rebind("rmi://hôte:port/NomService", objet)et assurez-vous que le port correspond à celui de votrermiregistry. - La résolution (Client) : Le client utilise
Naming.lookupavec exactement la même URL que celle utilisée par le serveur. - La sécurité : En RMI, le téléchargement dynamique de code nécessite un
SecurityManageret un fichier.policyqui accorde explicitement les droits d'accès réseau. Sans cela, des exceptions de sécurité (AccessControlException) bloqueront l'exécution lors de la tentative de connexion au registre. L'outilrmic, bien qu'historiquement demandé dans les anciens sujets universitaires, est aujourd'hui généré dynamiquement par la machine virtuelle Java. Il est néanmoins important de le mentionner si l'énoncé l'exige.
Commentaires
Aucun commentaire pour le moment. Posez la première question.