:: Enseignements :: Master :: Master TTT :: 2010-2011 :: Programmation réseau en Java ::
[LOGO]

Réalisation d'un forum (utilisation de Tomcat, Hibernate et Velocity) - Correction


Nous souhaitons réaliser un forum de discussion organisé sous la forme de fils. Afin de garantir une persistance des données, nous utilisons la bibliothèque Hibernate. Plutôt que de mélanger code HTML et code Java, nous choisissons de gérer des templates de pages avec Velocity. Nous séparons ainsi le modèle, la vue et le contrôleur (MVC).

Modèle de forum

L'application réalisée devra gérer un forum. Ce forum comprend un ensemble de fils de discussion où sont abordés des sujets particuliers. Chaque fil est démarré par un message initial (le message racine), les contributeurs répondant à ce message par d'autres messages qui peuvent eux-même faire l'objet de réponses. Les messages d'un fil sont donc organisés sous la forme d'un arbre. Pour simplifier, on pourra cependant considérer dans un premier temps que la hauteur de cet arbre est limitée à 2 niveaux (les messages initiateurs et les réponses directes à ces messages). Il est nécessaire également de maintenir des informations sur les contributeurs (nom, adresse email...).

Réfléchir à un modèle objet pour représenter les éléments manipulés (Message, Contributeur) par l'application.

Organisation du code

Nous créons un répertoire src/ où seront placés les sources de notre application. Le répertoire web/ abrite les fichiers statiques (telles que pages statiques ou images si l'on souhaite en mettre) et web/WEB-INF/web.xml le fichier de configuration de servlettes. Un répertoire config/ accueille les fichiers de configuration d'Hibernate et Velocity. Les templates seront placés dans un répertoire templates/. On utilise un fichier build.xml à la racine du répertoire pour réaliser une construction avec Ant. Il est nécessaire d'indiquer le chemin vers Tomcat dans la variable d'environnement CATALINA_HOME (on peut rajouter à cet effet l'instruction export CATALINA_HOME=/chemin/vers/tomcat dans son fichier .bashrc).

Fichier web.xml :

Fichier build.xml :

Une petite introduction à Hibernate

Hibernate est une bibliothèque permettant à partir de modèles définis sous la forme de classes Java de gérer la persistence des objets instantiés (ou POJO pour Plain Old Java Objects). Ainsi ces instances survivent à l'extinction de la JVM. Généralement, les instances sont stockées dans une base de données relationnelle (telle que PostgreSQL, MySQL, Oracle...) en utilisant une correspondance entre membres de classe et colonne de table. Hibernate est ainsi qualifié de "Object Relational Mapper". Les correspondances peuvent être spécifiées sous la forme d'un fichier XML ou plus simplement depuis Java 1.5 par des annotations incorporées au code (en utilisant la Java Persistence API dont on peut trouver la Javadoc ici).

À titre d'exemple, nous écrivons maintenant la classe Message modélisant un participant du forum :

Seule l'annotation @Entity et @Id pour indiquer la clé primaire sont indispensables. Hibernate déduit automatiquement le nom de table et les noms de colonnes d'après le nommage de la classe et des getters (mais on peut les expliciter également avec les annotations @Table et @Column).

En s'inspirant de l'exemple fourni, écrire l'autre classe du modèle : Contributeur.




Configuration de session Hibernate et test du modèle

Nous utilisons comme base de données support d'Hibernate HyperSonicSQL, une base de données relationnelle Open Source implantée en Java.
Télécharger la dernière version de HSqlDb sur cette page : décompresser le fichier zip. Celui-ci comprend un fichier JAR de classes compilées lib/hsqldb.jar qu'il faudra ajouter dans le répertoire lib de Tomcat.
La distribution binaire de Hibernate doit être également téléchargée (par exemple ici) pour placer les jars dans le répertoire lib/ de Tomcat

Il est nécessaire maintenant d'interfacer la base de données avec Hibernate (driver, paramètres d'accès à la base...) : on utilise pour cela un fichier hibernate.cfg.xml permettant de configurer une session-factory (qui permet de manipuler directement le fichier /tmp/forum.hsqldb sans nécessiter un serveur de base de données). On indique également dans ce fichier les classes de modèle que l'on utilise. On téléchargera aussi la classe singleton construisant à son initialisation une session factory vers la base et permettant de l'obtenir facilement avec la méthode statique getSessionFactory().

Fichier hibernate.cfg.xml :

Fichier HibernateUtil.java :

Tester le modèle en créant tout d'abord des contributeurs avec de nouveaux messages. Afficher ensuite les messages créés en relançant la JVM. Si tout fonctionne correctement, les objets ont bien été insérés au sein de tables de la base de données relationnelle. On consultera la Javadoc d'Hibernate concernant la classe Session et on s'intéressera spécifiquement aux méthodes suivantes :
  • beginTransaction() permet de débuter une transaction. Une fois celle-ci achevée on peut soit la valider par sa méthode commit(), soit l'annuler par rollback() (par exemple dans le bloc de gestion d'une exception) : ainsi soit toutes les opérations de mise à jour de la base sont réalisées, soit aucune.
  • save(Object) rend persistant un objet en l'enregistrant en base.
  • get(Class, Serializable) permet d'obtenir un objet en base en indiquant sa classe (Hibernate sait alors dans quelle table de la base le chercher) ainsi que son identifiant (généralement un Long). Cette méthode retourne null si l'objet n'existe pas. load(Class, Serializable) est une variante levant une exception en cas d'inexistence de l'objet.
  • Query createQuery(String) permet d'exprimer une requête en langage HQL afin de sélectionner des objets en base.
  • Criteria getCriteria(Class) offre une autre manière d'interroger la base d'objets pour la classe donnée en chaînant des Criterion (consulter la Javadoc pour des exemples). On peut ensuite obtenir la liste des résultats avec la méthode list() ou alors uniqueResult() si un seul objet est attendu.

A propos de l'utilisation de templates

Afin que les contributeurs puissent interagir avec notre application, nous devons retourner des pages leur permettant de lister tous les fils du forum, afficher les messages d'un fil et poster un nouveau message (nouveau fil) ou un message en réponse d'un autre. Nous souhaitons séparer le code de gestion du forum (récupération des messages, ajout de nouveau message...) - contrôleur - du code d'affichage des données et des formulaires d'envoi - vue -. Cette séparation est possible en utilisant un moteur de templates.

Un moteur de template pour la vue : Velocity

Apache Velocity est un moteur de template pour Java permettant de traiter des fichiers textes, notamment pour une application web (fichiers XML, HTML, courrier électronique...). On place dans le fichier template des variables à afficher (on peut utiliser également des appels de fonctions ainsi qu'accéder à des membres de classe via des accesseurs). On peut utiliser également des structures de boucles pour afficher des données itérables (tableaux, listes) ainsi que des structures conditionnelles pour n'afficher certaines parties que si une variable booléenne est satisfaite.

Télécharger une version binaire de Velocity et placer les fichier jar de la racine de l'archive vers le répertoire lib/ de Tomcat.

On pourra s'aider de la classe VelocityUtil suivante afin d'utiliser un template et de rediriger son résultat associé au contexte sur un Writer :

On ajoute le fichier de configuration suivant pour Velocity (config/velocity.properties). Celui-ci indique que l'on recherchera les templates en utilisant le classpath de Java.

Voici un exemple de template pour l'affichage des fils présents sur le forum (un tableau HTML indique le nom du sujet ainsi que son contributeur) que l'on place dans un répertoire templates/ de notre application :

Le template peut ensuite s'utiliser au sein de la servlette contrôlant l'affichage de la liste des fils du forum :

Écrire les templates pour le formulaire d'envoi de messages ainsi que pour l'affichage d'un message accompagné.




Contrôleur

Écrire le code pour la partie contrôleur chargée de traiter les requêtes : affichage des fils, affichage des messages et envoi de messages. Il s'agit de manipuler les classes du modèle afin de lire ou enregistrer des objets. On communique ensuite un contexte aux templates pour le remplacement des variables définies.




Félicitations, vous venez d'implanter un petit forum de discussion ! De nombreuses améliorations sont possibles. On pourra par exemple implanter un module permettant à un contributeur de lister ses messages et de lui offrir la possibilité de les modifier ou supprimer.