TP JAVA - RMI

Ada Diaconescu, Bertrand Dupouy et Frank Singhoff


SOMMAIRE


Déroulement du TP :
Il se divise en trois parties :

  1. la première partie : montre le fonctionnement élémentaire de RMI - à utiliser cet exemple pour mettre en place la seconde et la troisième partie ;
  2. la seconde partie : montre un modèle client-serveur - à développer  ;
  3. la troisième partie : montre un modèle agents mobiles - à développer.

Documentation :
Le support du cours se trouve ici

I. Utilisation élémentaire de RMI

RMI permet l'appel d'une méthode appartenent à un objet distant, c'est à dire géré par une autre JVM que la JVM locale.

L'objet distant (le serveur) qui propose l'appel de ses méthodes par des objets distants (des clients) doit offrir :

  1. une interface qui décrit l'ensemble des méthodes visibles à distance :
  2. une classe qui implémente cette interface ;
  3. une instance de cette classe - cette instance doit être enregistée sous un certain nom auprès du service de désignation de RMI (rmiregistry) :

L'appelant (le client) doit obtenir une référence sur l'objet distant (le serveur) :

  1. utiliser LocateRegistry afin d'obtenir une référence locale sur le registry distant (s'exécutant sur la machine du serveur) :
    Registry registry = LocateRegistry.getRegistry(host, port);
    L'objet registry se conduit en représentant local du serveur de noms distant.
  2. appel à registry.lookup(‹service_name›) qui interroge rmiregistry sur la machine distante.
    Le stub est téléchargé à ce moment (lors du lookup) ;
    Il faudrait convertir le type du stub retourné au type de l'objet distant (ex : Hello)

Les souches (fichier dont le nom est terminé par _stub) destinées au client et au serveur sont produites par rmic.
L'appel à la méthode distante est, dans les faits, traduite en appel local au stub fourni par le serveur. C'est ce dernier qui fait l'appel distant.

I.1 Les sources de l'application

Recopier les fichiers se trouvant dans http://perso.telecom-paristech.fr/~diacones/tp-rmi/exo1_tp.zip

Pour décompresser l'archive dans un répertoire exo1 :
unzip exo1_tp.zip -d exo1

On y trouve :

Commentaires sur le fichier Hello.java - l'interface distante - qui se réduit aux lignes suivantes :

    
	public interface Hello extends java.rmi.Remote{
		    String readMessage() throws java.rmi.RemoteException;
	}
  1. Remote signifie que la méthode readMessage (de toutes les classes implantant cette interface Hello) pourra être appelée depuis une JVM autre que la JVM locale.
  2. HelloServer.java implémente cette interface :
    public class HelloServer implements Hello{ ... }
    

I.2 Construire l'application

I.3 Exécution répartie avec partage du code via NFS

Pour commencer, on va exécuter l'application répartie dans un cas particulier, plus simple.
Dans ces cas, le partage de code - ex : pour les stubs - sera assuré par NFS (Network File System - installé sur les machines de TP) : le client et le serveur, même s'ils ne sont pas sur la même machine, partagent le même disque.

I.4 Exécution répartie avec partage de code à l'exécution

Dans le cas général - sans partage de code via NFS - il faut indiquer où se trouvent les stubs : Ceci permet le transfert de la classe stub (et de toute autre classe requise à l'exécution du client), à partir d'un endroit conu et accessible, vers la machine du client, pendant l'exécution.

Pour ce faire, il faut d'abord copier les fichiers à partager (ex : Hello.class) dans un répertoire dédié accessible par le rmiregistry du serveur :

Ensuite, lors du lancement du rmiregistry et du serveur rmi, il faudrait indiquer l'endroit où se trouvent ces classes - on utilisera le paramètre dédié codebase.

Option 1 - protocole file :

Option 2 - protocole http :

Attention : dans les commmandes ci-dessous, remplacer bien le texte <votre nom d'utilisateur unix> par votre vrais nom d'utilisateur.

I.5 Questions

Pour faciliter la résolution des exercices suivants, répondez aux questions ci-dessous en consultant le fichier produit par rmic -keepgenerated :

Voir dans le stub, exécuté localement par le client, l'appel à la méthode distante qui se fait par : ref.invoke où :

II. Utilisation du modèle client serveur

II.1 Modélisation de l'application

On se propose de simuler le scénario suivant : un cuisinier cherche des ingrédients pour préparer un repas au moindre coût.
Il va chercher les ingrédients un par un, en procédant ainsi :

Schéma du mécanisme d'interrogation :

exo_2 : client --> remote servers

Voici le contenu de l'interface Store.java :


/**
 * remote interface for Store services
 */
public interface Store extends java.rmi.Remote {
	
	/**
	 * get the price of the given ingredient
	 * note: this method can be called remotely 
	 * @param ingredient: the ingredient whose price should be returned
	 * @return: the price of the ingredient
	 * @throws java.rmi.RemoteException
	 */
	float getPrice(String ingredient) throws java.rmi.RemoteException;       
}


Remote signifie que la méthode getPrice des classes implantant cette interface Store peut être appelée depuis une JVM autre que la JVM locale.

StoreManager.java implémente cette interface :


public class StoreManager implements Store {
	...
}

et StoreClient.java utilise la méthode publique getPrice pour interroger les magasins.

II.2 Les sources de l'application

Copier les fichiers se trouvant dans http://perso.telecom-paristech.fr/~diacones/tp-rmi/exo2_tp.zip

Pour décompresser l'archive dans un répertoire exo2 :
> unzip exo2_tp.zip -d exo2

On y trouve :

II.3 Exercice

Dans cet exercice on vous demande d'abord de compléter le fichier source StoreClient.java puis de lancer toute l'application.

II.3.1 Compléter l'application

  1. Dans StoreClient.java, compléter les lignes où se trouvent des //TODO ..., c'est à dire :
  2. Une fois ceci fait, on compile et on lance l'application .

II.3.2 Construire l'application

II.3.3 Exécuter l'application

II.3.4 Exercices Optionnels

III. Utilisation du modèle agents mobiles

On reprend l'exemple précédent que l'on va traiter en mettant en oeuvre des agents mobiles.

III.1 Modélisation de l'application

Le client va créer un agent au lieu d'interroger lui-même les sites détenteurs de magasins.
Cet agent va passer de site en site, et sur chacun de ces sites entrer dans le magasin pour y lire le prix de l'ingrédient recherché. S'il y a lieu, il met à jour le prix minimal. Une fois tous les sites consultés, l'agent donne au client le nom du site qui propose l'ingrédient recherché au prix le plus bas, ainsi que ce prix.

Comment mettre en oeuvre cette application ?
Le client va lancer sa demande depuis un site qui sera appelé par la suite "initiateur". Ce client va initialiser un tableau de sites à parcourir, puis exécuter sur le premier site de la liste la méthode migrate, à laquelle il a passé l'agent en argument. Ceci fait migrer l'agent de hôte en hôte jusqu'à arriver de nouveau sur la hôte initiateur.

Le schéma suivant indique les accès aux objets distants lors du lancement initial de l'agent :

Deux types objets vont modéliser ces différents acteurs : Host et Agent.

  1. L'interface Host est la suivante :
    public interface Host extends Remote{	
    	void migrate(Agent agent) throws RemoteException;
    }
           
    Remote signifie que les méthodes de cet objet Host peuvent être appelées depuis une JVM autre que la JVM locale.

    Il y aura deux implémentations de cette interface Host  :

    1. une pour les sites détenant un magasin (HostImplem.java) : dans ce cas la méthode migrate consulte le prix puis fait migrer l'agent vers le site suivant ;
    2. une pour le site initiateur (Initiator.java) : dans ce cas la méthode migrate se contente d'afficher le nom du site proposant le prix le plus bas, ainsi que ce prix.

  2. L'interface de l'objet Agent est la suivante :
    public interface Agent extends Serializable{
    
    	void getMinPrice(String[] storeIngredients, Float[] storePrices);
    
    	String getNextHostName();
    	
    	boolean isNextInitiator();
    	
    	void displayResult();
    	 
    	int getRmiRegistryPort();
    }
    		

    Agent doit être une interface Serializable. En effet, Serializable indique que les objets de type Agent utilisés seront sérialisés et normalisés lors de l'appel distant ("marshalling").

    Les trois premières méthodes (getMinPrice, getNextHostName et isNextInitiator) servent aux sites consultés ; la quatrième (displayResult) est utilisée par l'initiateur.

On remarquera que ces deux interfaces Agent et Host sont complétement indépendantes de l'application actuellement traitée :

De façon plus générale, cette méthode permet d'écrire le code d'un serveur avant de savoir quelle application il va exécuter.

III.2 Les sources de l'application

Copier les fichiers se trouvant dans http://perso.telecom-paristech.fr/~diacones/tp-rmi/exo3_tp.zip

Pour décompresser l'archive dans un répertoire exo3 :
> unzip exo3_tp.zip -d exo3

On y trouve :

III.3 Implémentation des objets

III.3.1 Les sites consultés

Comme on l'a dit, un site consulté (HostImplem.java) va implémenter l'interface Host en faisant :
public class HostImplem implements Host{...}

Les points importants du programme (HostImplem.java), sont les suivants :

  1. l'appel au constructeur, qui met l'objet en attente d'éventuels appels distants ;
  2. la méthode main, qui appelle en particulier :
  3. la méthode migrate est implantée sous forme de thread pour que le traitement effectué par cet agent ne bloque pas le fonctionnement de l'hôte.

III.3.2 Le site initiateur

Le site initiateur est celui sur lequel se trouve le client qui déclenche le fonctionnement de l'agent mobile.
Ce site va, comme le précédent, implémenter de façon spécifique l'interface Host :

public class Initiator implements Host{ ... }

Les points importants du code sont les suivants :
  1. l'appel au constructeur qui met l'objet en attente d'éventuels appels distants ;
  2. la méthode main qui appelle en particulier :
  3. la méthode migrate se contente d'afficher un résultat. Elle sera appelée par le site sur lequel se trouve le dernier magasin à consulter.

Remarque importante : le stub permettant l'accès aux méthodes proposées par un objet distant est téléchargé lors de la recherche de cet objet par registry.lookup. Le téléchargement est fait par le serveur web donné par le paramètre server.codebase défini au lancement du serveur qui implémente l'objet distant.
Dans notre cas, HostImplem_stub sera téléchargé lors de l'exécution de la ligne suivante :

 
Host remoteHost = (Host)registry.lookup(hostServiceName);
ce stub sera utilisé pour accéder à la méthode distante migrate pendant l'exécution de :
remoteHost.migrate(agent);

III.3.3 L'agent

L'agent de notre application, appelé AgentImplem, est implémenté à partir de l'interface Agent, comme indiqué ci-dessous :

public class AgentImplem implements Agent{

	private Float myMinPrice = new Float(Float.MAX_VALUE);

	private String myIngredient="";

	private String myMinHost = "";
	
	...
}

et dans le code de l'initiateur (Initiator.java), on trouve :

public static void main(String args[]){
	...
	
	AgentImplem agent = new AgentImplem(Integer.parseInt(args[0]), hostsToVisit, args[1]);//args[0] - rmi-registry port; args[1] product; args[2] host {host} ...

	...	//obtain the first remote host to visit

	remoteHost.migrate(agent);//migrate the agent towards the first remote host
	
	...
}

Le site (HostImplem) qui offre la méthode migrate va créer un thread pour gérer l'agent, comme on l'indique ici :


public void migrate(Agent agent){
	AgentThread myThread = new AgentThread(agent, myStore);
	myThread.start();
}

la classe AgentThread hérite de Thread :

public class AgentThread extends Thread{ ... } 

III.4 Exercice

On vous demande :
  1. de compléter trois fichiers ;
  2. de lancer l'application ;
  3. de suivre son fonctionnement.

III.4.1 Compléter l'application

Fichiers à compléter :

Dans les fichiers AgentThread.java, Initiator.java et HostImplem.java compléter les lignes où se trouvent des //TODO ....

III.4.2 Lancer l'application

Note : n'oublier pas de compiler avant de tenter le lancement - utiliser l'exemple des exercices précédents.

Pour lancer l'application :

III.4.3 Suivre l'exécution de l'application

Lorsque l'application fonctionnera, suivre et expliquer les différents messages affichés par les hôtes distants ainsi que par l'initiateur.

III.4.4 Exercices Optionnels




©(Copyright) ada.diaconescu _at_ telecom-paristech.fr ; dupouy@enst.fr ; singhoff@enst.fr.