TP 3 : Design patterns côté serveur en Java

Ce TP fait suite au TP2. Dans cette partie, vous allez refactorer l'application réalisée et tirer un meilleur parti des possibilités du protocole HTTP pour améliorer les performances de votre application.

Objectifs pédagogiques

Date de rendu

Ce TP et le précédent sont à réaliser et à pusher dans la forge pour le dimanche 3 novembre 2019 à 23h59.

Outils

Refactoring de votre application

Une fois que l'utilisateur est connecté, la totalité de la logique applicative est gérée par billet.jsp, ce qui n'est pas très satisfaisant.

Pattern contexte

Le pattern contexte permet aux éléments d'une application de communiquer et d'échanger des informations. L'API Servlet fournit, avec le conteneur de servlets, un objet nommé ServletContext. Vous allez l'utiliser pour partager des données du modèle entre les différents éléments de votre application.

Par exemple, vous aurez besoin de la notion de groupe dans plusieurs CU : gestion des groupes, mais aussi gestion des billets. Le plus simple est de mettre le modèle à disposition de l'application dès l'appel de la servlet Init. Pour cela :

  1. Commencez par créer une classe Groupe : Un groupe contiendra un nom, une description textuelle, un propriétaire, une liste de participants et une instance de GestionBillets. Générez les constructeurs (vide et avec tous les paramètres), ainsi que les getters et setters avec votre IDE.
  2. Dans la méthode init(ServletConfig) de la servlet Init, récupérez le contexte applicatif, et ajoutez-y une instance de HashMap<String, Groupe> ; vous êtes libres de choisir le nom de l'attribut, mais "groupes" semble un bon candidat.
  3. Dans les JSP qui utilisent les groupes, vous pouvez y accéder avec une "action JSP" <jsp:usebean>, en utilisant l'attribut type (et non class) et le type de son interface : java.util.Map.

Testez.

Pattern MVC

Refactorez votre application pour appliquer un pattern MVC. Il existe 2 façons de le mettre en place, que vous allez utiliser dans 2 cas d'utilisation différents :

MVC pull-based

Mettez en place cette version du MVC pour les cas d'utilisation de gestion des groupes d'utilisateurs :

Pour permettre aux utilisateurs d'accéder à ce code :

Testez.

MVC Push-based

Dans cette solution :

Mettez en place cette version du MVC pour les cas d'utilisation de consultation et d'ajout de billets. Pour cela :

Testez.

Contrôle de l'accès aux vues

Maintenant que toutes vos JSP sont derrière un contrôleur, vous allez faire en sorte qu'on ne puisse plus y accéder directement. C'est une étape de la sécurisation de votre application.

  1. Déplacez vos JSP dans un sous-dossier (nommé par exemple "jsp") du dossier WEB-INF qui n'est pas exposé aux clients,
  2. Utilisez des redirections internes depuis vos contrôleurs vers le chemin local : WEB-INF/jsp/xxx.jsp

Pattern chaîne de responsabilité

Authentification

Créez un Filter qui vérifie, en fonction de l'URL de la page appelée (voir plus bas), que l'utilisateur est connecté (qu'il possède un login), avant de lui permettre d'accéder à l'application. Si ce n'est pas le cas, il sera redirigé vers le formulaire de connexion. Pour cela :

Attention : le filtre ne doit pas bloquer l'accès au formulaire de login. Vous configurerez votre application pour qu'il soit appliqué à toutes les URL sauf celle de ce formulaire.

Autorisation

Créez un second filtre qui vérifie, quand un utilisateur souhaite accéder à / poster un billet dans un groupe, qu'il est bien membre de ce groupe.

Remarque : l'ordre d'exécution des filtres par le conteneur de servlet est celui d'apparition dans le fichier web.xml. Il ne suffit donc pas d'annoter les filtres dans le code.

À ce stade, vous avez refactoré votre application en utilisant certains patterns vus en Génie Logiciel. Vous avez rendu le code plus lisible, en utilisant les différents éléments de l'API Java EE Web à bon escient.

Gestion du cache

L'état actuel de votre application est le suivant : toutes les 5 secondes, chaque client interroge le serveur, qui parcourt alors toute la liste de billets et re-génère dynamiquement un nouvel affichage contenant la totalité des billets enregistrés, qui sera renvoyé à chaque client via le réseau. Si le nombre d'utilisateurs est grand et/ou s'il y a de nombreux billets enregistrés dans la base, ce fonctionnement peut vite représenter une charge de travail importante pour le serveur, ainsi qu'une consommation inutile de bande passante réseau.

L'objectif de cette partie est d'améliorer le processus de récupération et d'affichage des billets échangés par les différents utilisateurs. Pour cela, vous allez permettre à votre serveur de déterminer s'il' doit recalculer l'affichage des billets pour un client, ou s'il n'y a pas lieu de le faire. Deux possibilités sont à votre disposition.

Utilisation des en-têtes HTTP

La plupart des clients renvoient au serveur un en-tête HTTP If-Modified-Since, indiquant la date de leur dernier chargement d'une page. Si cette date est postérieure à celle du dernier POST reçu, aucun nouveau billet n'est disponible. Plutôt que de renvoyer une réponse HTTP contenant une page complète, il suffit de renvoyer juste un en-tête HTTP, avec le code de statut 304 indiquant au client que la page n'a pas été modifiée.

Pour cela :

  1. Vérifiez que votre serveur envoie bien au client un en-tête Last-Modified à la génération de la réponse (sur certaines versions de Tomcat, le header est positionné par défaut sur les pages HTML et pas sur les JSP) ; sinon, rajoutez-le dans billet.jsp.
  2. Vérifiez que votre client vous renvoie bien cette valeur dans un en-tête If-Modified-Since lors de la requête d'actualisation de la page billet.jsp
  3. Lors de l'appel en GET de billet.jsp, comparez la valeur de l'en-tête If-Modified-Since reçu avec celle de l'attribut du contexte applicatif et réagissez en conséquence (génération de la page ou code de statut 304). Vous pouvez vous aider de ce tutoriel.

Il est possible que votre navigateur ne prenne plus en compte le header Refresh après réception d'un code de retour 304. Ne passez pas trop de temps à essayer de régler ce problème, qui ne se posera plus dans les versions ultérieures.

Utilisation des cookies

Si le numéro du dernier billet mémorisé (i.e. le nombre total de billets) est égal à celui du dernier billet envoyé à un client (i.e. le nombre total de billets au moment où le serveur lui a servi la réponse), cela signifie qu'aucun billet n'est arrivé depuis la dernière requête du client. Il est donc inutile de lui renvoyer la page.

  1. rajoutez / updatez un cookie chez votre client indiquant le numéro du dernier billet obtenu lors de l'envoi d'une réponse par Affichage.jsp
  2. lors de la réception d'une requête GET par billet.jsp, testez l'existence de ce cookie et comparez sa valeur au nombre total de billets mémorisés
  3. réagissez en conséquence (voir plus haut).
Indication :

Utilisez le getter de GestionBillets permettant de récupérer le nombre total de billets.

Vous avez maintenant commencé l'optimisation de votre application en termes de charge serveur et de bande passante réseau. Ce travail sera poursuivi lors des TPs REST et AJAX.

Déploiement

Déployez votre application sur votre VM avec le contexte v1. Elle doit donc être accessible aux URLs suivantes :

Aide :

Testez.

Si le comportement n'est pas le même à partir des 2 URLs, c'est que vous avez mis des URLs absolues quelque part.

Remarque : les URLs dans la configuration des servlets et des filtres (dans les annotations : url-patterns ou le fichier web.xml : servlet-mappping) ne sont pas absolues, même si elles commencent par un slash ; elles définissent un chemin à partir du contexte de déploiement de l'application.

Licence Creative Commons
Valid XHTML 1.0 Transitional