Document mis à jour le 16 avril 2001. Je suis ouvert à n'importe quelle suggestion pour améliorer ces matériaux. N'hésitez pas de contacter l'auteur, Jonathan Revusky.

This document in English
Este documento en español

Eplucher le servlet "Hello, Niggle"

Dans ce document, on suppose que vous avez réussi à franchir les étapes concrètes des instructions ici, c'est à dire, compiler et faire fonctionner l'exemple.

Ce servlet simple est composé des parties suivantes:

Le code Java

Dans l'API de servlets de base, on définit une sous-classe concrète de la classe abstraite javax.servlet.http.HttpServlet et on implémente les méthodes doGet() et doPost(), et aussi init() et destroy() s'il faut initialiser/libérer certaines ressources qui sont utilisés pendant la durée de vie du servlet. Par contraste, lorsqu'on écrit un servlet avec Niggle, normalement, il n'est pas besoin d'implémenter les méthodes que je viens de mentionner. La librairie dispose déjà d'une classe servlet com.revusky.niggle.servlet.NiggleServlet qui a ses propres implémentations de doGet(), doPost() et init() qu'on utilise directement.

Bien sûr, il reste la question: alors, où est-ce que je définis la fonctionalité de mon application web? La réponse, c'est que Niggle définit une autre classe abstraite de base, com.revusky.niggle.servlet.ServletInteraction. C'est cette classe qui est vraiment le point duquel on fait une sous-classe comparable dans le cadre Niggle: vous écrivez votre propre sous-classe concrète de ServletInteraction et vous mettez la fonctionalité de votre application là-dedans.

Maintenant je devrais indiquer une différence fondamentale entre la conception/architecture de Niggle et l'API de base de Sun. Dans l'API de base, un servlet, c'est essentiellement un objet genre singleton. Une instance est créée et cette instance traite de nombreuses pétitions au cours de sa vie. Par contraste, chez Niggle, une nouvelle instance de votre sous-classe de ServletInteraction est créée chaque fois qu'une méthode doPost() ou doGet() s'invoque en com.revusky.niggle.servlet.NiggleServlet. Autrement dit, un objet type ServletInteraction, c'est un objet jettable. On s'en sert une fois et on le jette. Il encapsule le traitement d'une seule pétition HTTP.

Maintenant, si vous ne l'avez pas encore fait, jettez un coup d'oeil au fichier HelloNiggleServlet.java. Peut-être que vous vous êtes étonné de voir que la classe est vide! Bon, ce qui se passe, c'est qu'il n'est pas besoin de définir de la fonctionalité ici, vu que, comme on vient d'indiquer, la classe NiggleServlet défnit déjà les méthodes qu'on implémente typiquement dans un servlet: doGet(), doPost() et init(). En fait, le seul rôle que cette classe joue ici, c'est que Niggle se sert du nom du servlet pour savoir la sous-classe de ServletInteraction à utiliser. Vu que cette classe servlet s'appelle HelloNiggleServlet, la sous-classe de ServletInteraction correspondante est censée s'appeler HelloNiggleServletInteraction. (Lisez cette note technique si vous voulez une explication plus complète de pourquoi il faut sous-classer NiggleServlet etServletInteraction.)

Ci-dessous, vous avez le code complet de HelloNiggleServletInteraction.java:


import com.revusky.niggle.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class HelloNiggleServletInteraction extends ServletInteraction {
    /**
     * Le constructeur obligatoire. Votre sousclasse de ServletInteraction doit
     * avoir un constructeur avec cette "signature", même si
     * on n'a pas a faire rien dedans (à part le fait d'invoquer
     * le constructeur de la classe parent, evidemment)
     */
     public HelloNiggleServletInteraction(HttpServletRequest request, 
                                          HttpServletResponse response, 
                                          NiggleConfig config)
     throws IOException {
         super(request, response, config);
     }
    
    /**
     * El "handler" par défaut. Ce méthode est invoqué
     * s'il n'y a pas de paramètre du type "action" parmi les 
     * paramètres de la pétition. D'autre façon, si la 
     * pétition définit le paramètre "action=foo"
     * Niggle suppose qu'il faut invoquer le méthode 
     * execFoo() method. Cet exemple, en étant très simple,
     * n'a qu'une seule action par défaut, et donc
     * on ne définit que le méthode execDefault().
     */
    public void execDefault() throws IOException {
        this.page = getPage("hello.nhtml");
        page.expose("title", "Hello, World");
        page.expose("message", "Welcome to the brave new niggle world!");
    }
}

Jettez un coup d'oeil au méthode execDefault(). Dans la première ligne, on fixe la variable page et dans les deux lignes suivantes, on expose un couple de variables de page -- title et message.

Donc, qu'est-ce qui se passe ici? Bon, probablemente, vous savez déjà que la philosophie de Niggle est basée sur une séparation très rigoureuse entre la présentation et la logique de l'application. Donc, les détails de présentation de n'importe laquelle sortie au client sont encapsulés dans une page modèle, qui reste complètement séparée de la logique de l'application comme telle. Donc, la classe ServletInteraction a une variable membre qui s'appelle page et celle-ci doit être défini en quelque moment dans un méthode genre execXXXX. Le code ci-dessus est un exemple de cette disposition.

Bref, l'instance de ServletInteraction a une variable membre qui s'appelle page qui encapsule notre page modèle. Cette variable est une instance de l'interface com.revusky.niggle.templates.Page.

La page modèle

L'explication antérieure laisse certaines questions sans réponse. Le code java de l'exemple suppose l'existence d'une page modèle qui correspond à "hello.nhtml" et d'ailleurs, on suppose l'existance d'un mécanisme pour y exposer les variables de page.

Bon, maintenant on est en mesure de rendre le tout très concret. Ci-dessous, vous avez la page modèle "hello.nhtml" qui utilise les deux variables que le code java définit.


 
<html> 
<head> <title>${title}</title> </head> 
<body> 
<P>Message: ${message}</P>
</body> 
</html> 

Remarquez que cette page modèle se ressemble pas mal à une page HTML. Bon, oui, essentiellement c'est une page HTML. Ce qu'il a de spécial, c'est la présence de deux chaînes ${title} et ${message} qui sont traitées de façon spéciale. Je suppose que vous vous êtes déjà rendu compte de ce qui se passe. Ces deux chaînes correspondent aux deux variables qu'on a défini dans le code java avec les invocations de page.expose().

Bon, la différence entre une page modèle comme celle-ci est une page HTML statique de toujours, c'est qu'une partie du contenu est définie de façon dynamique. Remarquez que, par défaut, Niggle se sert du moteur de pages Freemarker. Un aspect clé de la syntaxe de Freemarker, c'est que les chaînes entourées de ${...} sont déterminées dynamiquement.

Si vous avez étudié cet exemple et vous êtes confortable avec tous ses aspects, je vous encourage à passer au prochain exemple qui est une simple application genre "guestbook".


Annèxe: Une note sur certains problèmes de chargement de classes/ressources

Une question intéressante, c'est pourquoi il est nécessaire de faire une sous-classe de com.revusky.niggle.servlet.NiggleServlet si cette dernière contient déjà toute la fonctionalité dont on a besoin. Après tout, une des deux sous-classes qu'on définit finit par être vide! D'ailleurs, moi je serais le premier à reconnaître que le fait d'avoir deux points distincts de sous-classer, en parallèle, indique souvent des erreurs de conception chez une librairie de classes. Cela va à l'encontre de la règle de base de KISS (keep it simple, stupid).

Bon, tout de même, il y a deux raisons qui expliquent cette situation. La première est la plus importante, même si c'est dû à des considérations hyper-techniques. Vous voyez, un serveur de servlets veut bien isoler les divers contextes d'application les uns des autres. Et normalement, il le fait en faisant que chacun s'exécute dans son propre ClassLoader. Donc, si vous mettez niggle.jar dans le CLASSPATH global du système, donc, la classe com.revusky.niggle.servlet.NiggleServlet sera chargée par le ClassLoader global du système. Cela voudrait dire qu'il ne pourrait pas trouver les classes et les ressources qu'on aurait mises dans le répertoire <app-base>/WEB-INF/classes -- vu que ces derniers ne sont visibles que dans le contexte de leur propre ClassLoader! C'est donc, en obligeant le programmeur d'application à définir sa propre sous-classe de NiggleServlet (même si celle-ci est vide) et de la mettre dans <app-base>/WEB-INF/classes, on peut être sûr que les autres classes, telles que XXXServletInteraction peuvent être localisées.

Un avantage de moindre importance, c'est que, en obligeant le programmeur à créer une sous-classe de NiggleServlet, nous avons un nom de classe par défaut pour la sous-classe de NiggleServlet correspondante. Si vous appelez votre classe servlet XXX, donc, le système suppose par défaut que la sous-classe de ServletInteraction (qui contient vraiment la fonctionalité de l'application) s'appelle XXXInteraction. Je devrais aussi mentionner que, si vous ne voulez pas utiliser cette disposition automatique, vous pouvez spécifier un paramètre d'initialisation INTERACTION_CLASS qui donne un autre nom pour la sous-classe de ServletInteraction. En fait, chez des versions antérieures de Niggle, ce paramètre était obligatoire. Le gran avantage d'avoir un schème de nom par défaut, c'est qu'on peut créer un exemple minimaliste qui fonctionne sans aucune nécessité de définir des paramètres d'initialisation, vu que c'est souvent une source de problèmes lorsqu'il s'agit de faire fonctionner un servlet pour la première fois.

Retourner au texte principal