Master 2 TI - Méthodes de conception de systèmes d’information distribués - TP 2
Dans ce tp, vous allez apprendre à utiliser les infrastructures middlewares réparties CORBA et RMI, ainsi qu'un serveur d'application Java EE.
Même si CORBA est conçue pour permettre de communiquer à des objets hétérogènes, nous nous limiterons à de la programmation en Java, sachant que les
mêmes techniques que celles décrites ici existent dans les autres langages de
programmation. Ensuite, vous réutiliserez les concepts de ces technologies pour mettre en oeuvre des objets transactionnels distribués (EJB 3.0) et les services associés.
1. CORBA
1.1 CORBA : mise en route
Pour vous exercer, voici un premier exemple d’application
client
serveur implémentant les principes de communication avec CORBA, tiré de
celui proposé par SUN (http://java.sun.com/j2se/1.4.2/docs/guide/idl/GShome.html).
Un fichier zippé contenant un fichier IDL et les sources du
serveur/servant et du client est disponible localement sur :
http://www710.univ-lyon1.fr/~lmedini/TI1/Bonjour.zip.
- A partir de ce fichier, générez les fichiers correspondant
à l’interface exposée par le serveur à l’aide de l’outil idlj.
Ce code a été testé avec les deux modèles d’implémentation du serveur :
par héritage et par délégation (TIE). En d’autres termes, vous pouvez
indifféremment générer les fichiers avec l’option –fall ou –fallTIE. Pour info,
vous pouvez également utiliser les options –fserver / –fserverTIE pour
générer les fichiers serveur et –fclient
/ –fclientTIE
pour les fichiers client.
- Copiez ensuite les fichiers source Java dans le répertoire
« bonjour » créé par idlj et compilez l’ensemble des sources.
- Lancez ensuite le serveur de noms tnameserv, le
serveur et le client, dans cet ordre (voir cours 2) depuis 3 fenêtres
DOS différentes :
- tnameserv -ORBInitialPort 1050
- java bonjour/BonjourServer -ORBInitialPort 1050 -ORBInitialHost localhost (depuis le répertoire contenant le fichier .idl et le répertoire "bonjour")
- java bonjour/BonjourClient -ORBInitialPort 1050 -ORBInitialHost localhost (idem)
Remarques :
- Les manipulations ci-dessus doivent être faites à l’aide
une ligne
de commande dans une console (DOS / Linux). Sous DOS, les trois
applications (serveur de noms, serveur, client) doivent être lancées
dans cet ordre et dans des fenêtres différentes.
- Si plusieurs versions de Java sont installées sur votre
machine,
assurez-vous que tous les utilitaires (compilateur Java et IDL, machine
virtuelle Java) que vous utilisez sont bien issus de la même
installation.
1.2 CORBA
: conception
Vous allez maintenant intégrer la communication CORBA
à l’application développée au TP
précédent. Pour cela :
- précisez tout d'abord la définition IDL des interfaces exposées par le servant, et générez les différents fichiers (stub, skeleton, ORB) à l'aide du compilateur idlj,
- à l'aide d'un nouveau projet, créez un jar avec l'ensemble de ces fichiers que vous importerez dans votre projet Annuaire,
- dans votre projet Annuaire du TP 1 :
- supprimez le main de l'application
- faites deux projets séparés pour le serveur et le client
- dans le serveur, modifiez le code source pour qu'il récupère une référence sur le serveur de noms et qu'il y enregistre trois objets, représentant les composants qui implémentent les méthodes add, remove et list après les avoir instanciés dans le conteneur,
- modifiez le source du client pour qu’il envoie ses requêtes au serveur de noms,
- faites tourner séparément client et serveur, après avoir lancé le serveur de noms.
Remarques :
- Bien entendu, la structure de données HashMap n'existe pas
en IDL. A vous d'en définir une de manière aussi proche que possible, de façon à
pouvoir faire facilement un "mapping" entre les requêtes qui arriveront
au serveur et l'instance de HashMap utilisée en interne. Aide : http://www-itec.uni-klu.ac.at/~harald/corba/idldatattypes.html.
- Attention : normalement, il existe deux interfaces "Annuaire", l'une
dans votre projet initial, et l'autre dans les fichiers générés par idlj ; à
vous de voir comment éviter les confusions.
- Votre classe AnnuaireImpl, en plus des interfaces du TP précédent, va maintenant dépendre de deux éléments de ce package CORBA :
- elle implémentera l'interface Annuaire générée par idlj
- elle étendra une classe portant un nom ressemblant à AnnuairePOA (en fonction du nom de votre interface IDL).
Cela implique de lui adjoindre des méthodes de service (et notamment une méthode
shutdown).
Exemples de méthodes du serveur :
public class Serveur {
static AnnuaireImpl adder, remover, lister;
static ORB orb;
static POA rootpoa;
static NamingContextExt ncRef;
public static void main (String[] args) {
startConteneur(); //à vous de programmer
startServer(args);
referenceAnnuaire(adder, "add");
referenceAnnuaire(remover, "remove");
referenceAnnuaire(lister, "list");
run();
}
private static void startServer(String[] args) {
try{
// initialize the ORB
orb = ORB.init(args, null);
System.out.println("ORB initialized...");
// get reference to rootpoa & activate the POAManager
rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
Policy p[] = new Policy[7];
p[0] = rootpoa.create_thread_policy(ThreadPolicyValue.ORB_CTRL_MODEL);
p[1] = rootpoa.create_lifespan_policy(LifespanPolicyValue.PERSISTENT);
p[2] =
rootpoa.create_id_uniqueness_policy(IdUniquenessPolicyValue.MULTIPLE_ID);
p[3] =
rootpoa.create_id_assignment_policy(IdAssignmentPolicyValue.USER_ID);
p[4] =
rootpoa.create_servant_retention_policy(ServantRetentionPolicyValue.NON_RETAIN);
p[5] =
rootpoa.create_request_processing_policy(RequestProcessingPolicyValue.USE_DEFAULT_SERVANT);
p[6] =
rootpoa.create_implicit_activation_policy(ImplicitActivationPolicyValue.NO_IMPLICIT_ACTIVATION);
rootpoa.the_POAManager().activate();
System.out.println("POA initialized...");
// get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
// Use NamingContextExt which is part of the Interoperable
// Naming Service (INS) specification.
ncRef = NamingContextExtHelper.narrow(objRef);
System.out.println("NamingContext found...");
}
catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
}
private static void referenceAnnuaire(AnnuaireImpl annuaire, String name) {
try{
// register servant with the ORB
annuaire.setORB(orb);
// get object reference from the servant
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(annuaire);
annuaire.corba.Annuaire href =
(annuaire.corba.Annuaire) AnnuaireHelper.narrow(ref);
System.out.print("Servant " + name + " initialized...");
// bind the Object Reference in Naming
NameComponent path[] = ncRef.to_name( name );
ncRef.rebind(path, href);
System.out.println(" and bound.");
}
catch (Exception e) {
System.err.println("ERROR: " + e);
e.printStackTrace(System.out);
}
}
private static void run() {
System.out.println("Server ready and waiting...");
// wait for invocations from clients
orb.run();
System.out.println("Server Exiting ...");
}
2. RMI
2.1 RMI : mise en route
De la même manière, testez l'exemple qui permet de mettre en oeuvre un serveur de fichiers, accessible à partir de la liste des exemples de code :
http://java.sun.com/developer/codesamples/rmi.html (URL
directe :
http://java.sun.com/developer/technicalArticles/RMI/rmi_corba/).
2.2 RMI : conception
Reprenez une version fonctionnelle de votre application d'annuaire de sites Web, décomposée en 3 parties (Main, Serveur, Client). Par exemple, celle issue de la question 2.2 (uniformisation).
Dans votre projet Annuaire du TP 1 :
- supprimez le main de l'application
- faites deux projets séparés pour le serveur et le client
- dans le serveur, modifiez le code source pour qu'il récupère une référence sur le serveur de noms et qu'il y enregistre trois objets, représentant les composants qui implémentent les méthodes add, remove et list après les avoir instanciés dans le conteneur,
- modifiez le source du client pour qu’il envoie ses requêtes au serveur de noms,
- faites tourner séparément client et serveur, après avoir lancé le serveur de noms.
3. Enterprise JavaBeans
3.1 Le serveur d'applications JBoss
JBoss Application Server est le serveur d'applications Open Source qui implémente la spécification Java EE5 le plus utilisé. La dernière version stable en date est la version 5. Vous pouvez télécharger une version locale de la version 5.0 CR1 1 ici.
Si vous utilisez une machine de TP, le serveur d'applications JBoss doit être installé sur
cette machine. Testez l'assertion précédente. Sinon, ou si vous avez votre propre machine, téléchargez et installez JBoss AS (Application Server) 5.1, comme indiqué en ici. Ajoutez
ensuite le serveur à la liste des serveurs connus par Eclipse (Window
-> Preferences -> Server -> Runtime Environments).
Installation, configuration et démarrage
Trois configurations sont disponibles à l'installation : "all",
"default" et "minimal" ; la première nécessitant beaucoup
de ressources et la troisième ne possédant pas
d'interface Web, vous utiliserez la seconde.
Pour démarrer le serveur en configuration "default", ouvrez une fenêtre DOS, placez-vous dans le
répertoire bin de l'installation et utilisez la commande run -c
[config] ou "config est la configuration que vous voulez
démarrer. Si tout s'est bien passé, vous devez voir,
parmi les messages de démarrage du serveur, une ligne indiquant
que votre EJB (en fonction du nom du projet) a bien été déployé.
Interface Web de JBoss
Une fois cela fait, et ouvrez son interface Web (port par défaut
: 8080). Explorez-en les différentes fonctionnalités
(cela risque d'être vite fait, aucune interface d'administration
n'existant pour JBoss à ce jour).
Remarque : la configuration de JBoss utilise JMX (Java Management
Framework). Pour accéder aux informations concernant le
déploiement de votre EJB, il faut aller dans "System", "JMX
MBeans", puis dans le cadre de droite, trouver celui-ci dans
jboss.j2ee. Repérez le nom JNDI permettant d'y accéder.
3.2 Tutoriel EJB 3 sur JBoss
À partir de maintenant, pour démarrer le serveur, vous utiliserez l'interface de contrôle d'Eclipse.
Lisez et effectuez le tutoriel situé à l'adresse
http://www.eclipsetotale.com/articles/Introduction_EJB3_avec_Eclipse.html jusqu'à la partie "Tester l'EJB Entity" non incluse.
Remarque : ce tutoriel a été rédigé pour JBoss 4.2. La seule chose qui change avec JBoss 5 est le nom de deux fichiers JAR à intégrer dans le client : il faut remplacer jbossall-client.jar par jboss-client.jar et jboss-ejb3-client.jar.
Arrivé à cette partie, vous devez avoir deux classes (Produit et
GestionDeStockBean) et une interface (GestionDeStock).
- Créez deux projets EJB différents, et placez l'EJB entité dans l'un et l'EJB session dans l'autre.
- Supprimez le répertoire META-INF du second projet (mais pas du premier).
- Exportez ces deux projets sous forme de fichiers JAR
- Créez le fichier persistence.xml ci-dessous :
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="DefaultDS" transaction-type="JTA">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
- Ouvrez le jar du
projet contenant l'EJB entité et placez ce fichier XML dans le
répertoire META-INF (vous pouvez en revanche supprimer le fichier
manifest)
- Créez un nouveau projet de type Enterprise
application vide, supprimez-en le répertoire META-INF et exportez-en
directement le fichier EAR.
- Ouvrez cette archive (avec un logiciel de compression-décompression) et :
- ajoutez le JAR de l'EJB session à la racine
- créez un répertoire "lib" à la racine
- placez le fichier JAR de l'EJB entité dans ce répertoire "lib".
- Placez ce fichier EAR dans le répertoire "deploy" de la configuration default de JBoss et démarrez le serveur.
- Vérifiez que vos EJB ont été déployés.
- Créez et testez le client comme indiqué dans le tutoriel.
3.3 Utilisation des EJB 3 dans votre application d'annuaire
Vous allez créer un projet de type "Enterprise application", dans lequel vous importerez vos sources. Vous n'avez plus besoin de votre serveur,
celui-ci étant remplacé par le serveur d'applications et le conteneur d'EJB. Modifiez vos classes d'Annuaire et de Site pour qu'elles
implémentent les types d'EJB ad hoc et qu'elles soient gérées par le conteneur d'EJB.
Remarque : gardez à l'esprit que vos annuaires sont désormais les clients de l'EJB Site.
Comme précédemment, générez en plusieurs
fois le fichier EAR correspondant à votre application.
Placez le fichier JAR de votre projet EJB dans le répertoire "deploy" de la configuration par défaut.
3.4 Programmation des clients
Client Web
À partir du code fourni au premier TP, créez une ou
plusieurs servlet(s) pour interroger le bean et renvoyer les
résultats dans une interface Web. Le serveur fourni avec JBoss
étant Tomcat, vous pouvez packager tout cela sous forme
d'application Web (fichier .war) et le déployer dans JBoss de la
même façon que votre application EJB.
Le container Web dans lequel vous travaillerez étant dans le
même serveur d'applications, le contact du serveur JNDI se fera
automatiquement. Vous pouvez donc utiliser les annotations
présentées en cours pour appeler votre EJB.
Client RMI
Reprenez le client de la question 2.2 ("RMI : conception") et adaptez-le à la communication avec les EJB déployés dans voter serveur d'application.
4. Pour aller plus loin
Client dans un conteneur ad hoc (facultatif)
Il existe également des conteneurs légers
implémentant les annotations JEE5 et permettant de
définir "facilement" des clients standalone pour les EJB en Java
SE. C'est le cas d'Application Client Container (ACC). Ce conteneur
fonctionne à peu près comme ceux
expérimentés au TP 1 : il possède des
mécanismes de réflection permettant d'injecter des
dépendances entre objets, dont il se sert notamment pour
l'annotation @EJB. Par rapport à PicoContainer, le conteneur ACC
gère en plus l'exécution de l'application en proposant
différents services JEE5, tels que l'accès distant
à des serveurs de noms, via RMI/IIOP.
Sun GlassFish propose en standard du JDK JEE5 un script nommé
appclient.bat qui devrait se trouver sur les machines de TP qui permet
de démarrer un ACC pour un client donné. La syntaxe
appclient -client [Chemin/nom_du_client] permet de l'exécuter.
Créez un projet de type J2EE/Application Client, reprenez le
code du client modifié au TP 1 et intégrez-le dans un
conteneur AAC. Faites tourner ce client dans un process
indépendant de votre serveur d'applications et faites en sorte
qu'il se connecte à votre EJB et puisse l'utiliser.
Utilisation de CORBA dans d'autres langages
Vous
pouvez également utiliser
d'autres
langages pour faire communiquer entre eux des objets
hétérogènes ; pour cela, il vous
faudra une
implémentation de CORBA spécifique au langage de
programmation choisi.
- MICO
(pour Mico Is
COrba) propose ce type d'outils, de manière gratuite et
totalement compatible avec CORBA 2.0.
- OmniORB
propose également des librairies faciles à
installer pour
C++ et Python. Un exemple (en français) de client Hello
World et
de la manière de le compiler en C++ est disponible ici.
Téléchargez
et installez OmniORB (voir plus haut) et réalisez un client
d'interrogation en C++.