TP Applications
Distribuées : mise en oeuvre en RMI
HARMONISATION
Mireille Blay-Fornarino
Annick Fron
Anne-Marie Pinna-Dery
Septembre 2008
Le but de ce
TP est de
mieux appréhender la
problématique de passage par valeur/ par référence.
Nous nous appuyons sur RMI pour mettre en oeuvre ces concepts.
Installation
Pour ce TP utilisez le JDK 1.6.
Vous pouvez utiliser eclipse avec le JDK 1.6 Il suffit de modifier
éventuellement le
paramètre de la JVM :
Menu Window/Preferences
Onglet Java/Installed JRES
et bouton "Add" pour ajouter
une nouvelle version de Java
Liens utiles
Exercice pour débutant
Si vous êtes
vraiment débutant en Java et RMI, faire le TP suivant : Getting
started with RMI http://java.sun.com/javase/6/docs/technotes/guides/rmi/hello/hello-world.html
Exercice Préparatoire
Cet exercice a pour objectif de poser sur un exemple simple les
concepts de fabrique
et d'objets distants.
Définir une application
telle que :
- Un joueur est défini par :
- un pseudo
- une adresse email
- une capacité à sélectionner un nombre
compris entre 1 et 10.
- Une CentraleDeJeux
permet de :
- d'enregistrer des joueurs
- de connaitre le nombre de points d'un joueur à partir
de son pseudo, si le joueur n'est pas connu de la centrale, lève
une exception
- lancer une partie à partir des pseudos des joueurs
- une partie se déroule en 5 tours, chaque joueur
donne un nombre et celui qui a donné le plus grand nombre gagne
1 point.
- renvoie une string décrivant la partie
- Un Serveur de jeu créé une centraleDeJeux HasardSophia , puis attend que des
joueurs s'incrivent et lancent des parties.
- Chaque joueur s'enregistre auprès de la centrale HasardSophia
- Un Client demande le lancement d'une partie
à HasardSophia et
affiche l'état des joueurs.
- Décider de votre architecture en prenant soin de
séparer les codes d'implémentation des interfaces, le
code du serveur, ...
- Implémenter votre application
- Tester la.
Si vous rencontrez des problèmes pour implémenter cette
application, vous pouvez, après avoir évidemment
essayé de l'implémenter seul, vous inspirez des codes suivants.
A
la fin de cet exercice, vous devez savoir :
- Décider d'une architecture distribuée :
quelles sont les
entités qui doivent être enregistrées auprès
d'un serveur de nom,
comment rendre une entité accessible à distance, quel est
le rôle de la fabrique, quelle est la durée de
vie d'une entité distribuée, ...
Une partie de cartes
Sur la base de l'exercice préparatoire précédent,
fabriquez une partie de cartes : la bataille.
L'objectif est d'introduire la notion d'objets sérialisés.
A présent les joueurs ne choisissent plus un nombre au hasard
mais une carte parmi un ensemble de cartes dont ils disposent :
- celui qui a la plus forte carte ramasse les cartes de tous les autres,
- si un joueur n'a plus de carte, il pioche et si la pioche est
vide, il ne joue plus.
- Pour simplifier, nous considérons que le gagnant est celui qui
a toutes les cartes.
Vous pouvez reprendre pour vous aider le code d'un jeu
de bataille écrit en non-distribué, vous n'avez plus qu'à
le
"distribuer" !
Tu me fends le coeur !
Notre objectif est de pouvoir lancer le serveur de jeu et
chacun des joueurs sur des
machines différentes.
Pour les utilisateurs d'eclipse, ceci se traduit par des projets
différents, pour les autres par des répertoires
différents.
Pour le tester, il y a deux modes :
- soit TestBataille qui
crée trois joueurs et lance le jeu,
- soit TestSynchro, qui
attend que 3 joueurs s'inscrivent avant de lancer le jeu.
1) Conception de
l'application
Faites un schéma UML de l'application : Diagramme de classes et
scénario.
Ce diagramme doit vous aider à comprendre le code existant.
a) Déterminez d'abord la nature de chacune des classes
d'implémentation pour pouvoir la distribuer :
- classes Remote
- classes Serializable
- classes Locales
b) Posez vous les questions
suivantes :
- Listez les exceptions, et
réfléchissez à la
manière dont elles sont traitées en local ou en remote.
- Déterminez les modifications à apporter à
une
classe normale pour qu'elle devienne Remote.
- Prenez en compte le cas des variables statiques.
- Triez les méthodes à mettre dans l'interface
Remote.
- Pensez à utiliser l'outil serialver
pour vérifier qu'une classe est Serializable.
2) Plan de
déploiement
Déterminez ensuite combien vous allez utiliser de rmiregistry,
ainsi que leur localisation sur les différentes machines et le
nombre d'objets à y stocker, ainsi que leur nommage.
Déterminez quelles classes doivent être
déployées sur chaque répertoire : pour le serveur
de jeu et pour chacun des
joueurs.
3)
Implémentation
Implémentez alors les classes supplémentaires ou modifiez
les classes existantes, et écrivez une courte notice de
jeu permettant à un joueur de se connecter et de jouer.
Notes :
- Pour rendre les instances d'une classe accessible à
distance, celle-ci doit implémenter une interface qui
étend la classe Remote.
- Sous eclipse vous pouvez extraire directement l'interface
(vérifiez quand même que seules les méthodes qui
ont du sens à distance sont extraites)
- Pour rendre les instances d'une classe sérializable, cette
dernière doit déclarer implémenter Serializable.
4) Chasse aux
tricheurs
A présent, on fait la chasse aux tricheurs. Est-il possible de
vérifier avant chaque tour de jeu qu'on n'a pas plus de quatre
as dans le jeu ? Serait-il possible à un joueur de créer
une carte à l'insu du serveur de jeu ?
5) Liste
partagée
Un joueur peut-il s'inscrire en cours de partie ? Quelles sont les
conséquences ?
6) Variables statiques
Si dans la classe Carte, vous initialisez les constantes statiques par
code et non dans la déclaration,
ces valeurs seront-elles transmises aux joueurs ?
7) Pioche
Comment faire pour qu' à présent chaque fois qu'un joueur
n'a plus de carte il puisse piocher une carte
a) en passant par le jeu
b) en accédant directement à la pioche
8) Carte NULL :
amélioration du code
Que pensez-vous des comparaisons avec une CARTE_NULL ?
a) pourquoi .equals ne peut pas fonctionner à distance?
b) comment pourriez-vous pallier cet inconvénient et rendre le
code plus propre?
9) Synchronisation
des joueurs
A quoi sert la classe : TesterBatailleServeur (à renommer!!!!)
pourquoi introduire une attente dans le lanceur du jeu de bataille ?
Quel probleme voyez-vous à lancer en même temps plusieurs
fois TesterBatailleServeur
10) Si vous peinez trop
Vous pouvez vous reporter aux éléments de corrections
suivants
Erreurs
fréquentes
- Stub inaccessible au rmiregistry
java.rmi.ServerException: RemoteException
occurred in server ...
java.rmi.UnmarshalException: error
unmarshalling ...
java.lang.ClassNotFoundException:
Le stub est bien accessible au serveur MAIS PAS AU rmiregistry
se traduit par
java.net.ConnectException
- Oubli du constructeur pour le RemoteObject
se traduit par :
HelloImpl.java:20: unreported exception java.rmi.RemoteException in default constructor
public class HelloImpl extends UnicastRemoteObject implements Hello {
^
- rmiregistry déjà lancé ailleurs
se traduit par :
java.rmi.server.ExportException: Port already in use: 1099
On peut lancer un autre registry mais sur un autre port.
- CalculatorImpl n'hérite pas de UnicastRemoteObject
S'il n'hérite pas de UnicastRemoteObject, il n'est pas Remote
donc on lui demande d'être Serializable ...
Trouble: java.rmi.MarshalException: error marshalling arguments; nested
exception is:
java.io.NotSerializableException: CalculatorImpl
Elements de
correction
Fichier
descriptif
Code
de la bataille Distribuée
Code
ServeurDeBataille
CodeServeurDeJoueur
Version avec répertoires
séparés
entites
jeu
joueur
serveur
de jeu
serveur
de joueur