Java component that extends the capabilities of a server.
Partie intégrante d’une application web
Web archive - WAR
Structure spécifique
Dans WEB-INF
, inaccessible en HTTP
En dehors de WEB-INF
, accessible en HTTP via le chemin vers la ressource :
/page/hello.html
est accessible via http://domain:port/page/hello.html
Pages d’accueil
Déclaration de servlets
Déclaration de filtres
Paramètres d’initialisation
etc.
Situé dans répertoire WEB-INF
Référence un schéma XSD
Dépend de la version de Java EE
Optionnel !
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
</web-app>
Corrélé à une URL
Modèle requête réponse :
Activé par l’envoi d’une requête
Retourne (souvent) une réponse
Le cycle de vie est géré par le serveur d’applications
Instanciation
Service
Destruction
Il peut exister n instances à un moment t
On ne sait pas quelle instance est appelée
Double lien :
URL - Nom logique
Nom logique - Classe pleinement qualifiée
<web...>
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>ch.frankel.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web>
package ch.frankel;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet { ... }
Strict, par exemple /hello
Générique, mais limité :
/hello/*
*.do
Spécifiques :
/*
/
Créer une classe enfant de HttpServlet
Implémenter la méthode qui correspond au verbe HTTP désiré
doGet()
doPost()
etc.
Représentation Java d’une réponse HTTP :
Envoi d’un code erreur HTTP
Redirection vers une autre URL
Gestion des en-têtes HTTP
Gestion des cookies
Ecriture :
de caractères (texte)
d’octets (binaire)
public class SendErrorServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req,
HttpServletResponse res) {
res.sendError(500);
}
}
Le navigateur reçoit un code HTTP 30x :
Lecture de l’URL configurée dans l’en-tête HTTP Location
Nouvelle requête GET
à cette URL
Type | Code statut HTTP |
---|---|
Permanent | 301 |
Temporary | 302 |
public class RedirectServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req,
HttpServletResponse resp) {
resp.sendRedirect("https://www.google.com");
}
}
Permet d’écrire du texte dans le flux de réponse
Nécessite de spécifier le type MIME
public class PrintServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest req,
HttpServletResponse resp) {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println("<html><body>");
out.println("Hello world!");
out.println("</body></html>");
}
}
Permet d’écrire des octets dans le flux de réponse
Nécessite de spécifier le type MIME
Génération dynamique :
d’images
de tableaux Excel
etc.
Représentation Java d’une requête HTTP :
GET /java/index.html HTTP/1.1
Host: formations.github.io
Accept: image/gif, image/jpeg, */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Analyse de la requête :
Accès à l’URL (protocole, domaine, paramètres, etc.)
Accès aux cookies
Accès aux en-têtes HTTP
Accès à la session HTTP
Accès au RequestDispatcher
Fait suivre la chaîne de traitement au prochain servlet
Inclut la réponse du prochain servlet dans le flux actuel
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
req.getRequestDispatcher("/resource")
.forward(req, resp);
}
}
Ecrire du HTML via le PrintWriter
est laborieux
Prévisualisation impossible pour les web designers
Format texte
Au premier appel, le container :
Translate la JSP en servlet
Compile le servlet
<!DOCTYPE html>
<html>
<head>
<title>Hello world</title>
<body>
<% Object n = request.getAttribute("name"); %>
Hello <% out.println(n); %>
</body>
<!DOCTYPE html>
<html>
<head>
<title>Hello world</title>
<body>
Hello <%= request.getAttribute("name") %>
</body>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Hello world</title>
<body>
Hello <c:out value="requestScope.name" />
</body>
N’importe quel code Java
Balisé par <%
et %>
Equivalent à out.println()
Balisé par <%=
et %>
Type | Exemple |
---|---|
Import |
|
Inclusion |
|
Taglib |
|
Ignoré dans le processus de compilation
Ne sera pas disponible dans le HTML généré
Balisé par <%--
et --%>
Déclaration de méthodes et d’attributs
Pour information (très peu utilisé)
Balisé par <%!
et %>
Variable | Type |
---|---|
|
|
|
|
|
|
|
|
Variable | Type |
---|---|
|
|
|
|
|
|
|
|
|
|
Principe de séparation des responsabilités
Rôle | Composant | Fonctionnalité |
---|---|---|
Contrôleur | Servlet | Gère le flux de contrôle |
Vue | JSP | Affiche la page |
Modèle |
| Gère les données |
Comment transférer des données au travers du pipeline de traitement d’une requête ?
Par exemple, du contrôleur (servlet) à la vue (JSP)
Servlet dédié au traitement des données (validation, enregistrement, etc.)
JSP dédiée à l’affichage du HTML
"Bravo M. Dupont, vous avez bien été enregistré"
Il faut transférer les données (titre et nom) du servlet à la JSP
A chaque HttpServletRequest
est associée une Map<String, Object>
.
Le servlet stocke l’objet comme attribut de requête sous la clé key
Le servlet forward à la JSP
La JSP récupère l’attribut de requête sous la clé key
Le transtype dans le type correct
Nom | Objet | Objet implicite JSP |
---|---|---|
Application |
|
|
Session |
|
|
Requête |
|
|
Page |
|
|
HTTP est un protocole déconnecté
Impossible de savoir si 2 requêtes proviennent de la même session navigateur
Utilisation d’un cookie JSESSIONID
Cookie géré automatiquement par le serveur d’applications
Ensemble d’attributs spécifiques à une session navigateur
Stockés en mémoire
Attention :
Aux collections sans limite de taille
Aux objets de taille trop importante
HttpSession session = req.getSession();
session.setAttribute("person", new Person("John", "Doe"));
// in another component (servlet, jsp, filter)
Object object = session.getAttribute("person");
Person person = (Person) object;
Dans le modèle MVC, la JSP joue le rôle de vue
Mais les scriptlets permettent tout
Par exemple, d’accéder à la base de données
Une spécification décrit comment créer une librairie
La librairie comporte des fonctionnalités spécifiques
Intégrable dans les JSPs
Taglib | Balise XML à laquelle est associée une classe Java compilée |
Tag file | Balise XML à laquelle est associée un fichier |
Expression Language - EL | Exécute du code arbitraire |
${sessionScope.cart.numberOfItems}
${param['mycom.productId']}
${requestScope['javax.servlet.forward.servlet_path']}
${!empty param.Add}
etc.
Spécifiée par un fichier Tag Library Descriptor
Dans le répertoire META-INF
du JAR de taglib
Référence les classes de Taglib
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="urn:foo:bar"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://foo.bar/bar.xsd"
version="2.1">
<display-name>Foo Bar</display-name>
<tlib-version>1.0</tlib-version>
<short-name>foo</short-name>
<uri>http://foo.com/bar</uri>
<tag>
<name>bar</name>
<tag-class>ch.frankel.BarTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>baz</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
<%@ taglib uri="http://foo.com/bar" prefix="foo" %>
<html>
<body>
<foo:bar baz="hello">
<h1>Title</h1>
</foo:bar>
</body>
</html>
Core - c
Internationalisation - fmt
Tag Library Validator - tlv
(SQL - sql
)
Test
Itération
Affichage
etc.
<% List person = (List) request.getAttribute("persons"); %>
<% for (Person person: persons) { %>
<%= person.getName() %>
<% } %>
<c:forEach var="person" items="${requestScope.persons}">
<c:out value="${person}" />
</c:forEach>
First, they provide the ability to encapsulate recurring tasks in reusable units. […] Second, filters can be used to transform the response from a servlet or a JSP page.
Authentification
Logging
Conversion d’image
Compression de données
Cryptage
etc.
Corrélé à une URL
Intercepte :
Soit la requête
Soit la réponse
Soit les 2
Comme pour un servlet
<web...>
<filter>
<filter-name>Authenticate</filter-name>
<filter-class>ch.frankel.AuthenticateFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Authenticate</filter-name>
<url-pattern>/secure/*</url-pattern>
</filter-mapping>
</web>
package ch.frankel;
@WebFilter("/secure/*")
public class AuthenticateFilter implements Filter { ... }
Créer une classe enfant de Filter
Dans doFilter()
:
Appeler chain.doFilter()
pour passer à la prochaine étape
Sinon, le flux s’arrête là
@Override
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) req;
HttpSession session = httpReq.getSession();
if (authenticated(session)) {
chain.doFilter(req, res);
} else {
HttpServletResponse httpRes = (HttpServletResponse) res;
httpRes.sendError(401, "Not authentified");
}
}
Authentification
Autorisations
Intégrité des données
Confidentialité des données
Non-répudiation
Audit
Le modèle de sécurité en Java EE 7 est plutôt réduit
Authentification et autorisations d’accès à une URL
La majeure partie de la configuration est propriétaire côté serveur d’applications
Si une URL est protégée
Et le client n’est pas authentifié
Alors, déclenchement de la procédure d’authentification
Utilisateurs
Groupes
Rôles
<security-role>
dans le descripteur de déploiement
<security-role>
<role-name>ADMIN</role-name>
</security-role>
<security-constraint>
dans le descripteur de déploiement
Type | Balise |
---|---|
Ressource web |
|
Contrainte d’autorisation |
|
<security-constraint>
<web-resource-collection>
<web-resource-name>Partie sécurisée</web-resource-name>
<url-pattern>/secure/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
</auth-constraint>
</security-constraint>
Mode | Configuration |
---|---|
Popup native |
|
Formulaire |
|
Certificat SSL |
|
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
Composant | Contrainte |
---|---|
Formulaire |
|
Champ de login |
|
Champ de mot de passe |
|
<form method=post action="j_security_check">
<input type="text" name= "j_username" />
<input type="password" name= "j_password" />
<input type="submit" value="Login" />
</form>
HttpServletRequest.isUserInRole(String role)
HttpServletRequest.getUserPrincipal()
HttpServletRequest.getRemoteUser()
(HttpSession.invalidate()
)
Java EE implémente la Programmation Orientée Evènements
Divers évènements liés au cycle de vie :
Démarrage/arrêt de l’application
Gestion des attributs dans les différents contextes
etc.
Possibilité de s’abonner à ces évènements
Classe associée | Evènement |
---|---|
| Démarrage de l’application |
Arrêt de l’application |
Classe associée | Evènement |
---|---|
| Ajout d’attribut |
Suppression d’attribut | |
Remplacement d’attribut |
Classe associée | Evènement |
---|---|
| Initialisation de la requête |
Destruction de la requête |
Classe associée | Evènement |
---|---|
| Ajout d’attribut |
Suppression d’attribut | |
Remplacement d’attribut |
Classe associée | Evènement |
---|---|
| Création de la session |
Destruction de la session |
Classe associée | Evènement |
---|---|
| Ajout d’attribut |
Suppression d’attribut | |
Remplacement d’attribut |
Classe associée | Evènement |
---|---|
| Objet stocké en session |
Objet supprimé de la session |
Classe associée | Evènement |
---|---|
| Activation de la session |
Déactivation de la session |
<web...>
<listener>
<listener-class>
ch.frankel.ASessionListener
</listener-class>
</listener>
</web>
@WebListener
public class SessionListener implements HttpSessionListener {
}
@WebListener
public class DatabaseInitializationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// Create sample data in database
}
@Override
public void contextDestroyed(ServletContextEvent event) {}
}
Gérer les exceptions qui ne sont pas traitées par le servlet
De manière transversale
A chaque type d’exception
Ou à chaque type d’erreur HTTP
Correspond une page d’erreur
Ou une page d’erreur catch all
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/WEB-INF/npe.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/404.jsp</location>
</error-page>
<error-page>
<location>/WEB-INF/errors.jsp</location>
</error-page>
Garder les pages d’erreur simples
Dans la JSP, il est possible d’accéder à l’exception via la variable exception
Nécessite la directive <%@ page isErrorPage="true" %>
i18n fait partie de Java SE
Mais utilisé dans Java EE
Via la librairie JSTL fmt
Tag | Description |
---|---|
| Set la locale depuis une variable |
| Charge le bundle et le stocke dans une variable |
| Affiche le message internationalisé |
<%@ taglib prefix="fmt"
uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:setLocale value="${request.locale}" />
<fmt:setBundle basename="ch.hesge.messages" var="msg" />
<html>
<body>
<ul>
<li><fmt:message key="key.one" bundle="${msg}" /></li>
<li><fmt:message key="key.two" bundle="${msg}" /></li>
</ul>
</body>
</html>