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 EnglishDans 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:
HelloNiggleServletInteraction.java
hello.nhtml
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
.
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".
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.