Nous avons vu que les servlets permettent facilement d'exécuter du code Java pour traiter une requête HTTP.
Cependant, l'API servlet n'est pas très pratique pour générer une réponse orientée texte (telle qu'une page HTML).
Les Java Server Pages (JSP) ont été la première solution introduite pour offrir une alternative plus simple pour l'ecriture
de patron (template) de réponse.
Développer des JSP avec TomEE
TomEE est un serveur livré avec une configuration par défaut conçue pour un environnement de production.
Pour le développement de JSP, nous allons voir qu'il est plus intéressant de configurer le serveur de test afin qu'il
prenne en compte nos modifications dans les JSP à la volée sans qu'il soit nécessaire de redéployer l'application.
Pour des raisons de performance, la prise en compte à chaud des modifications n'est pas le comportement par défaut de TomEE.
Pour activer ce comportement, il va falloir modifier la configuration du serveur. Pour cela, allez dans le répertoire
d'installation de TomEE et ouvez le fichier conf/web.xml. Aux alentours de la ligne 229, vous allez trouver la
déclaration suivante :
Détruisez maintenant votre instance de serveur dans votre IDE (dans Eclipse, supprimez le serveur dans votre vue "Servers") et créez une nouvelle
instance d'un serveur TomEE afin de prendre en compte ces modifications.
Attention, si vous faites une mauvaise manipulation, votre serveur peut ne plus démarrer correctement.
Exercice : première JSP
Objectif
Créer et afficher dans un navigateur une JSP. À la racine de l'application Web (répertoire src/main/webapp dans le projet Maven),
créer un fichier index.jsp avec le contenu suivant :
<!DOCTYPE html><html><head><title>Java EE</title></head><body><p>Notre première JSP.</p></body></html>
Éditer le fichier pom.xml du template et modifier la balise artifactId pour spécifier le nom de votre projet.
Modifier ensuite la section <developers> pour indiquer vos nom et email.
Intégration du projet dans Eclipse
L'intégration du projet dans Eclipse suit la même procédure que celle vue lors de l'introduction à Maven
Si on s'en tient à l'exercice précédent, une page JSP ressemble exactement à une page statique (comme une page HTML) : il n'en est rien !
En fait le serveur transforme automatiquement une page JSP en une servlet équivalente à :
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* En réalité la servlet créée à partir d'une page JSP
* est plus complexe que le code ci-dessous.
*/publicclassindexextendsHttpServlet{
@OverrideprotectedvoiddoGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write("<!DOCTYPE html>\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>Java EE</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" <p>Notre première JSP.</p>\n");
out.write(" </body>\n");
out.write("</html>\n");
}
}
Tous les accès HTTP à la JSP ne retournent pas directement la page que nous avons écrite mais exécutent la servlet qui a été générée par le serveur.
Nous allons voir maintenant comment nous pouvons ajouter du contenu dynamique dans une JSP.
EL : Expression Language
Java EE intègre l'expression language (EL). L'EL est directement utilisable dans une JSP.
Il s'agit d'un langage permettant de manipuler des expressions avec
une syntaxe simplifiée. L'EL n'est pas un langage de programmation. Il se limite à l'évaluation d'expression
qui retourne une valeur (boléenne, numérique, chaîne de caractères, objet,...).
Une expression en EL est facilement reconnaissable car elle est délimitée par ${ }.
${myObject} : l'attribut portant le nom "myObject"
${myObject.myProperty} : équivalent à myObject.getMyProperty()
${myObject["myProperty"]} : équivalent à myObject.getMyProperty()
${myList[0]} : pour accéder au premier élément d'une liste
${myMap["key"]} : pour accéder à la valeur associée à la clé "key" d'une map
Les opérateurs dans l'expression language
L'EL dispose également de différents opérateurs. Certains opérateurs peuvent s'écrire indifféremment avec un symbole ou une abbréviation :
+
Addition (attention + ne peut pas être utilisé comme opérateur de concaténation de chaîne de caractères comme en Java)
-
Soustraction
*
Multiplication
/
div
Division
%
mod
Modulo
==
eq
Égalité
!=
ne
Inégalité
<
lt
Inférieur à
>
gt
Supérieur à
<=
le
Inférieur ou égal à
>=
ge
Supérieur ou égal à
&&
and
Et logique
||
or
Ou logique
!
not
Négation
empty
vraie si l'expression à droite est nulle, une chaîne vide, un tableau vide ou une map vide.
De plus, il est possible d'utiliser les parenthèses et l'opérateur logique ternaire : condition ? si vrai : si faux
Les objets implicites dans une JSP
Dans une JSP, il existe une liste pré-définie d'objets qui sont directement
accessibles dans en EL :
pageScope
Map permettant d'accéder aux différents attributs de portée (scope) page. Les attributs de portée page
correspondent aux attributs déclarés dans la page.
requestScope
Map permettant d'accéder aux différents attributs de portée (scope) request.
sessionScope
Map permettant d'accéder aux différents attributs de portée (scope) session.
applicationScope
Map permettant d'accéder aux différents attributs de portée (scope) application.
param
Map permettant d'accéder aux paramètres de la requête HTTP.
paramValues
Map permettant d'accéder aux paramètres de la requête HTTP sous forme de tableau. Pratique si un paramètre est transmis
plusieurs fois dans une requête.
header
Map permettant d'accéder aux valeurs des en-têtes HTTP de la requête.
headerValues
Map permettant d'accéder aux valeurs du Header HTTP de la requête sous forme de tableau. Pratique si un en-tête est transmis
plusieurs fois dans une requête.
cookie
Map permettant d'accéder aux Cookies transmis dans la requête HTTP.
initParam
Map permettant d'accéder aux paramètres d'initialisation (déclarées dans le web.xml).
pageContext
L'objet PageContext de la page JSP.
On trouve notamment dans cet objet les attributs request et response (respectivement de type HttpServletRequest et HttpServletResponse).
On peut, par exemple, afficher dynamiquement des informations liées à la
requête dans une JSP :
<!DOCTYPE html><html><head><metacharset="ISO-8859-1"><title>Test JSP</title></head><body><p>Bienvenue sur <strong>${header["Host"]}</strong> !</p><p>Vous accédez actuellement à la page <strong>${pageContext.request.requestURI}</strong></p><p>Votre navigateur Web est : <strong>${header["user-agent"]}</strong>.</p><p>${empty param ? "Vous n'avez pas envoyé de paramètre au serveur"
: "Vous avez envoyé des paramètres au serveur"}</p><p>${empty cookie ? "Vous n'avez pas envoyé de cookie au serveur"
: "Vous avez envoyé des cookies au serveur"}</p></body></html>
La résolution de portée des attributs dans une JSP
Nous avons vu qu'il existe dans une JSP les objets implicites : pageScope, requestScope, sessionScope et applicationScope.
Ces objets permettent d'acceder aux attributs de leur portée respective. Par exemple :
${sessionScope["utilisateur"].nom}
Il est également possible de référencer directement l'attribut utilisateur dans une page JSP :
${utilisateur.nom}
Dans ce cas, l'attribut utilisateur est recherché successivement dans les portées page, requête, session (si elle existe) et enfin application.
Le premier attribut trouvé portant ce nom est utilisé.
Expression Language et gestion des exceptions
Un apport majeur de l'EL par rapport à du code Java, est la façon dont sont traités les références nulles
et les dépassements d'index dans les tableaux.
Si une référence d'un attribut ou d'une propriété est nulle, l'expression n'échouera pas, elle retournera simplement vide.
${unAttribut.unePropriete.uneAutrePropriete}
L'expression ci-dessus est évaluée à vide si unAttribut est nul ou unePropriete est nulle ou uneAutrePropriete est nulle.
Cela rend le code plus robuste et ne nécessite pas de vérifier un à un les élements d'une expression.
Pour les tableaux, accéder à un index qui dépasse la borne supérieure est également évalué à vide.
${paramValues["unParametre"][1000]}
Les directives de JSP
Il est possible d'utiliser les directives page, include et taglib.
La directive page
La directive page permet de donner des informations sur le contexte d'exécution de la JSP. Il est recommandé de placer
cette directive sur la première ligne de la JSP.
Cette directive accepte entre autres les attributs :
contentType
Le type MIME du contenu généré par la JSP. La valeur par défaut est "text/html".
pageEncoding
L'encodage de la page, la valeur par défaut est "ISO-8859-1". Attention, le fait de préciser l'encodage
dans le header HTML n'est pas suffisant pour une JSP. En effet, le header HTML est interprété par la client
mais pas par la JSP. L'attribut pageEncoding de la directive page est donc là pour
informer le conteneur Web de l'encodage à utiliser réellement pour envoyer la réponse au client.
errorPage
Contient un lien vers une page JSP à utiliser si une exception se produit lors du traitement de cette JSP.
Dans ce cas, c'est le traitement de la page JSP d'erreur qui sera retourné au client.
isErrorPage
Indique si la JSP est une JSP d'erreur. Dans ce cas, errorData est disponible dans
le pageContext. errorData est de type javax.servlet.jsp.ErrorData
La directive include
La directive include permet d'insérer le contenu d'une page (fichier statique ou une autre JSP) au moment
de la compilation de la JSP (i.e. la conversion de la JSP en servlet).
<%@includefile="fragment.html" %>
L'inclusion se fait à l'endroit où la directive est placée.
Pour la directive taglib, nous y reviendrons ultérieurement.
Les balises d'action JSP
JSP définit un ensemble de balises (action tags) pour réaliser des actions simples. Ces balises commencent toutes par jsp:
<jsp:useBean/>
Permet de référencer ou de créer un objet Java.
Pour référencer un objet (un java bean), on utilise les attributs suivants
id : donne le nom de l'attribut dans la page qui référencera l'objet
beanName : donne le nom de l'attribut qui contient l'objet
scope : donne la portée dans laquelle se situe l'attribut (page, request, session, application)
type : le type Java complet (avec le nom de package) de l'objet
L'utilité de <jsp:useBean> pour référencer un attribut est limité.
Depuis l'introduction de l'EL, il est possible d'accéder facilement aux attributs avec des expressions de la forme ${nomAttribut}.
Pour créer un objet, on utilise les attributs suivants
id : donne le nom de l'attribut qui référencera l'objet
scope : donne la portée dans laquelle l'attribut sera stocké (page, request, session, application)
class : le type Java complet (avec le nom de package) de l'objet
<jsp:setProperty/>
Permet de positionner les propriétés d'un objet
à partir d'une valeur (attribut value de la balise) ou d'un paramètre de la requête (attribut param de la balise).
<jsp:getProperty>
Affiche dans la page le contenu d'une propriété d'un attribut.
On obtient le même résultat en utilisant une EL, on préfèrera donc cette dernière qui est une forme plus courte
et plus expressive :
${u.nom}${u.age}
<jsp:include/>
Permet d'inclure dynamiquement une page (statique ou une autre JSP). À la différence de la directive <%@include %>,
la balise action <jsp:include> est interprétée à chaque exécution de la JSP. Cela signifie
que l'adresse de la page à inclure peut être calculée dynamiquement grâce à une EL.
Cette balise est similaire à un appel à RequestDispatcher.include
<jsp:forward/>
Permet de déléguer le traitement de la requête à une autre ressource de l'application.
Cette balise est similaire à un appel à RequestDispatcher.forward.
Les Taglibs et la JSTL
En plus des balises d'action, l'utilisation des JSP peut être enrichie grâce à l'inclusion de bibliothèques
de balises : les Tag Libraries (taglib en abbrégé).
Le développement de telles bibliothèques dépasse le cadre de ce cours. Par contre nous allons voir comment
utiliser la bibliothèque standart fournie par le conteneur Web : Java Standard Tag Library (JSTL).
Pour inclure une bibliothèque de balises dans une JSP, on utilise la directive taglib :
L'attribut uri désigne le nom de la bibliothèque. Cette URI ne pointe pas nécessairement sur une
adresse Internet. Il s'agit simplement d'un nom unique permettant au conteneur Web d'identifier
l'implémentation de la bibliothèque. L'attribut prefix désigne un identifiant quelconque qui devra être placé
devant chaque balise de la bibliothèque afin de l'identifier sans ambiguïté.
Ce mécanisme suit le même principe que les espaces de nom XML.
La JSTL est découpée en cinq bibliothèques, chacune devant être incluse par
une directive taglib.
Cette bibliothèque contient des balises pour la gestion des conditions et des boucles (c:if, c:forEach)
et d'autres balises permettant une programmation simplifiée dans les JSP.
À noter que la bibliothèque core propose également une balise out qui
permet de réaliser un échappement des caractères réservés en HTML. Les caractères comme < et > seront
automatiquement transformés en < et > :
Il est également possible de générer des URL absolues grâce à la balise url. Cette balise
se charge de reconstruire l'URL à partir du contexte racine de l'application. Par exemple, pour le code JSP suivant :
Si l'application est déployée dans le contexte racine monappli, alors le code HTML généré par la JSP sera :
Cette bibliothèque fournit des balises pour formater les données (date, nombre, ...) mais
également pour assurer une internationalisation de l'application (gestion de la langue en fonction des préférences du client).
Cette bibliothèque n'introduit pas de nouvelle balise mais des fonctions utilisables avec
l'expression language. Ces fonctions servent principalement à manipuler les chaînes de caractères
ou à connaître la taille d'un tableau (fn:length)
Comme son nom l'indique, cette bibliothèque permet d'exécuter des requêtes SQL dans les JSP.
Nous verrons par la suite que son utilisation reste très limitée car dans une architecture Java EE,
les accès aux bases de données sont généralement gérés par des composants dédiés.
Cette bibliothèque permet de lire et de manipuler des documents XML directement dans les JSP.
Exercice : traitement d'un formulaire
Objectif
Réaliser une application Web qui fournit une page d'accueil présentant un formulaire Web.
Le formulaire permet de saisir son nom et son prénom et de soumettre une requête POST au serveur.
Le serveur doit répondre par une page HTML retournant le nom et le prénom saisis ainsi que la date de reception sur le serveur.
Utilisez uniquement des JSP pour réaliser cet exercice.
Éditer le fichier pom.xml du template et modifier la balise artifactId pour spécifier le nom de votre projet.
Intégration du projet dans Eclipse
L'intégration du projet dans Eclipse suit la même procédure que celle vue lors de l'introduction à Maven
Exercice : Implémenter un formulaire en plusieurs étapes avec session
Objectif
Réaliser une application qui permet à un utilisateur de s'inscrire en donnant des informations personnelles.
La saisie d'information doit se faire en plusieurs étapes :
Une première page Web demande à l'utilisateur de saisir son nom et son prénom. Puis l'utilisateur
clique sur le bouton "suivant".
Une nouvelle page demande à l'utilisateur de saisir son adresse dans un seul champ texte. Puis
l'utilisateur clique sur le bouton "valider".
Finalement une page récapitulative s'affiche avec toutes les informations de l'utilisateur (nom, prénom et adresse).
Pour réaliser cette inscription en plusieurs étapes, vous utiliserez le mécanisme de session Web
pour conserver temporairement les données saisies par l'utilisateur.
Utilisez uniquement des JSP pour réaliser cet exercice.