SERVLET (2/2)

Transcription

SERVLET (2/2)
Maintien de l'état
z Le
protocole HTTP est un protocole sans
état
SERVLET (2/2)
impossibilité de garder des informations
d’une requête à l’autre
z impossibilité d'identifier un client d’un autre
entre 2 requêtes
z
z Solutions
=
les champs cachés
z la réécriture d'URLs
z les cookies
z les sessions
z
61
60
Champs cachés et réécriture d'URL
z
Cookies
Champs cachés
z
z
Information dans la page HTML cachée à l'utilisateur
<input type="hidden" name="secret" value="Chuuuut !" />
z
z
Positionné dans la balise <form>
Transmis au serveur lorsque le formulaire est soumis
z
z
z
Informations textuelles envoyées par un serveur Web à un
client (navigateur). Lorsqu’un client reçoit un cookie, il le
stocke localement et le renvoie ensuite au serveur chaque
fois qu’il accède à une page sur ce serveur
Paires de 'nom/valeur' échangées via les en-têtes HTTP
Note : suivre un lien hypertexte en dehors du formulaire n'envoie
pas l'information au serveur
HTTP/1.1 200 OK
...
Set-Cookie: JSESSIONID=50BAB1DB58D5F833D78D9EC1C5A10C5;Path=/myDir
Réécriture d'URL
z Sauver l'état dans l'URL
http://www.monsite.com/mapage?user=toto
z Chaque URL est une requête de type "GET"
z URL parfois longue car elle doit contenir toute
l'information liée à l'état
GET /myDir/servlet/SessionServlet HTTP/1.1
...
Cookie: JSESSIONID=50BAB1DB58D5F833D78D9EC1C5A10C5
z
z
Les cookies ont une durée de vie paramétrable
Utilisations :
z
z
62
Suivi de session basé sur un identifiant unique
Personnalisation de l'application en fonction des choix du client
63
Cookies
z
Cookies
z
Limites :
z
z
z
z
z
Pour les manipuler : javax.servlet.http.Cookie
z
Ne contiennent que des chaînes de caractères
Transmis à chaque requête => impact sur les
performances
Déconseillés pour une utilisation sécurisée car la
valeur des paramètres est passée en clair dans la
requête
Le navigateur peut être paramétré pour les refuser
La taille et le nombre des cookies sont limités par le
navigateur pour des raisons de sécurité
z
z
Cookie(String name, String value) : constructeur
String get/setName() : retourne/attribue le nom du cookie
String get/setValue() : retourne/attribue la valeur du cookie
z
z
z
z
caractères non autorisés : espace [ ] ( ) = , " / ? @ : ;
setDomain(String) : assigne le nom du domaine (Généralement
laissé vide car le nom du serveur est assigné par défaut)
String get/setPath(String) : retourne/attribue le path où
s'applique le cookie
get/setMaxAge() : délai restant avant expiration du cookie (en
seconde)
z
Par défaut, durée de vie d'un cookie = la connexion courante
64
Cookies
z
Cookies
...
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
En pratique, la servlet :
z
récupère les cookies liés à l'application dans la
requête
z
z
Cookie[] HttpServletRequest.getCookies()
HttpServletResponse.addCookie(Cookie)
modifie un cookie
z
z
PrintWriter out = res.getWriter();
String sessionId = null;
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals("sessionid")) {
sessionId = cookies[i].getValue();
}
}
}
if (sessionId == null) {
sessionId = new java.rmi.server.UID().toString();
Cookie c = new Cookie("sessionid", sessionId);
c.setMaxAge(60 * 60 * 24 * 365); // 1 an
res.addCookie(c);
out.println("Bonjour le nouveau");
} else {
out.println("Encore vous"); }
}
out.flush(); out.close();
ajoute un cookie
z
z
65
ajouter un cookie avec le même nom et le même path
supprime un cookie
z
Cookie.setMaxAge(0) et l'ajouter dans la réponse
}
66
67
Sessions
z
Les sessions
z
z
z
z
z
Sessions
z
Permettent de stocker les informations des clients côté serveur
et non du côté client
Pallient aux limites des cookies : stockage d'objets complexes
et pas de limite de taille
Attention : elles ne sont pas persistantes et se terminent à la
déconnexion du client !
z
z
Objets de type javax.servlet.http.HttpSession
Obtenir une session à partir de HttpServletRequest
z
z
z
Manipuler la session à partir de HttpSession
z
z
HttpSession getSession()
retourne la session associée à l’utilisateur
HttpSession getSession(false)
la session est récupérée ou null si elle n'existait pas déjà
HttpSession getSession(true)
la session est récupérée ou ouverte si elle n'existait pas déjà
z
setAttribute(String, Object)
création/remplacement d'un attribut
Object getAttribute(String)
retourne un attribut par son nom
removeAttribute(String)
supprime un attribut par son nom
Enumeration getAttributeNames()
retourne le nom de tous les attributs
invalidate()
invalide l’ensemble de la session et libère tous les
objets qui y sont associés
68
Sessions
z
69
Sessions : suivi
z
Un compteur de visites au cours d'une même session
Il existe 2 moyens de gérer les sessions (transparent) :
z
z
...
public class SessionServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
z
Cookies
z
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
HttpSession session = req.getSession();
Integer count = (Integer)session.getAttribute("count");
if (count == null) {
count = new Integer(1);
} else {
count = new Integer(count.intValue() + 1);
}
session.setAttribute("count", count);
out.println("Vous avez visité cette page " + count + " fois.");
z
z
}
70
A chaque session créée, le serveur envoie un identificateur
(cookie) correspondant au client. Ensuite le client renverra ce
cookie au serveur lors de sa prochaine requête permettant ainsi
au serveur d'identifier le client => mais les cookies peuvent être
désactivés !
Réécriture des URLs
z
}
à l'aide de cookies (par défaut)
à l'aide re la réécriture d'URL
Le serveur ajoute l'identificateur de session à la fin de chaque
URL des pages envoyées par celui-ci
Cette technique a un coût pour le serveur : il doit personnifier
l'URL en y incluant l'identifiant de session
71
Sessions : suivi
z
Sessions : web.xml
Réécriture des URLs
z
2 méthodes existent pour individualiser les URL:
z
z
String HttpServletResponse.encodeRedirectURL(String)
...
res.sendRedirect(res.encodeRedirectURL("/servlet/login");
...
z
<session-timeout> : permet de définir la durée en
minutes pendant laquelle la session d'un utilisateur
reste active
...
<web-app>
...
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
String HttpServletResponse.encodeURL(String)
...
out.println("<A HREF=\"/servlet/toto\">Go</A>");
// à remplacer par ...
out.print("<A HREF=\"" + res.encodeURL(req.getContextPath() +
"/servlet/toto") + "\">Go</A>");
web.xml
http://localhost:8080/monAppli1/servlet/toto;jsessionid=c0o7fszeb1
Ces méthodes réécrivent l'URL uniquement si le client n'accepte pas
les cookies. Dans l'autre cas l'URL reste inchangée
z Limitations des 2 approches : pas de données volumineuses,
caractères non autorisés, longueur URL, données visibles (sécurité)
z
z
72
ServletContext : Partage d'informations
ServletContext : Partage d'informations
Les servlets d'une même application peuvent partager des
informations au moyen du contexte ( ≠ session ! )
z Le conteneur de servlet gère un contexte par application
z Le contexte peut servir à communiquer :
z
z
z
z
z
de l'information entre servlets d'une même application (ou non)
avec le conteneur de servlet (Obtenir des ressources sur le serveur,
écrire dans un fichier de log)
z
z
Possibilité de partager des informations entre contextes web
d'un même serveur
Solution : la Servlet recherche un autre contexte à partir de
son propre contexte
z
Le contexte implémente l'interface ServletContext et est accessible
par getServletContext() via HttpServlet
z Partage d’information inter-servlets :
z
Partage d’information inter-applications
z
z
z
73
z
Obtenir des ressources de l'application à partir du
contexte courant
z
Enumeration getAttributeNames()
retourne l’ensemble des noms de tous les attributs liés
Object getAttribute(String)
retourne l’objet sous le nom indiqué
void setAttribute(String, Object)
lie un objet au nom indiqué
void removeAttribute(String)
supprime l’objet lié sous le nom indiqué
z
z
z
74
ServletContext getContext(String uri) : obtient le contexte à partir
d’un chemin URI (chemin absolu)
Set getResourcePaths(String) : Liste des chemins à partir
d'un chemin ("/META-INF/", "/WEB-INF/", etc.)
String getRealPath(String) : obtenir le chemin absolu à partir
d'un chemin relatif de l'application
URL getResource(String)
InputStream getResourceAsStream(String)
75
ServletContext : Gestion des traces
z
ServletContext : web.xml
z
Produire des logs dans Tomcat (<tomcat>/logs/):
void log(String)
void log(String, Throwable)
z
z
...
public void doGet(...)...{
...
getServletContext().log("A message");
z
Servlet
Des informations de contexte peuvent être définies
dans le descripteur web.xml
Ces informations sont accessibles par toutes les
servlets de l'application web
String ServletContext.getInitParameter(String)
web.xml
try {
// ...
} catch (Exception e) {
getServletContext().log("An exception occurred", e);
}
...
<web-app>
...
<context-param>
<param-name>annee</param-name>
<param-value>2007</param-value>
<description>une description ...</description>
</context-param>
</web-app>
...
...
2007-10-06 21:12:54 A message
2007-10-06 21:12:54 An exception occurred
java.io.IOException
at com.master2.MyServlet.doGet(Unknown Source)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
Tomcat
...
76
Collaboration de servlets
z
z
z
z
z
Délégation : une Servlet peut renvoyer une requête entière à
une autre servlet
Inclusion : une Servlet peut inclure du contenu généré par une
autre servlet
z
z
z
z
path est le chemin relatif ou absolu d'une ressource (servlet, JSP,
fichier statique, …) ne pouvant pas sortir du contexte courant
z
78
Effectuée par le serveur sur le même serveur
void RequestDispatcher.forward(ServletRequest, ServletResponse)
Effectuée par le serveur sur un autre serveur
void RequestDispatcher.sendRedirect(String)
Possibilité de transmettre des informations lors de la
redirection
z
Sur la requête : getRequestDispatcher(String path)
Identifier le contexte extérieur : utilisation de getContext(String uri)
Utiliser ensuite getRequestDispatcher(String path)
Note : les chemins sont uniquement en absolu
Redirection de la requête d’une servlet vers une autre
ressource sur le serveur
Types de redirection :
z
Délégation de compétences
Meilleure abstraction et architecture logicielle MVC
z
Pour distribuer en dehors du contexte courant, il faut :
z
z
Interface gérant la collaboration :
javax.servlet.RequestDispatcher
Une instance de RequestDispatcher est obtenue :
z
z
z
Les avantages sont :
z
z
Collaboration de servlets : redirection
Les servlets peuvent partager ou déléguer le contrôle de
la requête
Deux types de collaborations :
z
z
77
en utilisant les attributs de requête via la méthode
setAttribute(...)
en attachant des paramètres à l’URL
79
Collaboration de servlets : redirection
z
Collaboration de servlets : redirection
z
Illustration de redirection interne
Illustration de redirection externe : alternative 1 : redirection différée
du client vers une autre URL
...
public class ClientMove extends HttpServlet {
static final String NEW_HOST = "http://www.newhost.com";
public class Emetteur extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
req.setAttribute("nom", "toto"); // getAttribute(...) pour
// retrouver la valeur
RequestDispatcher disp =
req.getRequestDispatcher("/recepteur.do?age=22");
disp.forward(req, res);
// Ne rien faire sur req et res
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String newLocation = NEW_HOST + req.getRequestURI();
res.setHeader("Refresh", "5; URL=" + newLocation);
out.println("Changement d'URL : <A HREF=\"");
out.println(NEW_HOST+ "\">"+ NEW_HOST +"</A><BR>");
out.println("Vous serez redirigé dans 5 secondes...");
}
public class Recepteur extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
out.println(req.getParameter("age")); // chaîne de caractères
out.println(req.getAttribute("nom")); // objet
}
}
...
}
z
Illustration de redirection externe : alternative 2 : redirection
immédiate du client vers une autre URL :
z
sendRedirect(String)
80
Collaboration de servlets : inclusion
z
z
public class Regroupage extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML><BODY>");
void RequestDispatcher.include(
ServletRequest req, ServletResponse res)
RequestDispatcher dispat = req.getRequestDispatcher("/inclus.do");
dispat.include(req,res); out.println("<BR />");
req.setAttribute("bonjour", "Bonjour");
dispat.include(req,res); out.println("<BR />");
La différence avec une redirection est :
z
z
z
Collaboration de servlets : inclusion
Inclusion du contenu d’une ressource dans la
réponse courante (Jsp, servlet, fichier texte ...)
z
81
la servlet appelante garde le contrôle de la réponse
elle peut inclure du contenu avant et après le contenu
inclus
Possibilité de transmettre des informations lors
de l'inclusion
z
en utilisant les attributs de requête via la méthode
setAttribute(...)
82
req.setAttribute("bonsoir", "Bonsoir");
dispat.include(req,res); out.println("<BR />");
out.println("</BODY></HTML>");
}
}
public class Inclus extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
PrintWriter out = res.getWriter();
if(req.getAttribute("bonjour") != null) {
out.println(req.getAttribute("bonjour"));
if (req.getAttribute("bonsoir") != null) {
out.println(req.getAttribute("bonsoir"));
} else { out.println("Pas Bonsoir"); }
} else { out.println("Rien"); }
}
}
83
Servlet et Thread
z
z
Servlet et Thread
z
Une servlet ne possède qu'une seule instance
(singleton) qui est attaquée par plusieurs
threads à la fois (1 thread / client)
Problèmes :
z
Plusieurs threads peuvent traverser la méthode
service() en même temps => ne sont pas thread
safe ni les variables d'instance, les contextes, les
sessions
z
z
Cas d'un client qui clique plusieurs fois sur un bouton
"submit", ouvre plusieurs navigateurs, etc.
z
Accès concurrents sur une ressource : modification
d’un fichier commun, etc.
Solution 1 : utilisation de synchronized
public class MyThread extends HttpServlet {
private int i = 0;
public void doGet(…)…{
synchronized(this) {
int tmp = ++i;
}
public class MyThread extends HttpServlet {
…
public void doGet(…)…{
User user = new User();
HttpSession session = req.getSession(true);
synchronized(session) {
session.setAttribute("user", user);
}
…
En général :
z
z
Les variables doivent être locales aux méthodes
Les attributs de classes ne peuvent être que de constantes de
configuration initialisées dans la méthode init()
84
Servlet et Thread
z
web.xml : Gestion des erreurs
Solution 2 :
z
Utiliser l’interface SingleThreadModel implémentée par la
servlet qui permet d’indiquer au moteur de servlets que l’on
souhaite qu’une instance de la servlet ne soit attaquée que par
un Thread à la fois
z
85
Cette interface ne comporte aucune méthode à implémenter, elle ne
représente qu’un drapeau pour le moteur de servlet
...
<web-app>
web.xml
...
<servlet-mapping>
<servlet-name>ErrorServlet</servlet-name>
<url-pattern>/servletErr</url-pattern>
</servlet-mapping>
...
<error-page>
<exception-type>javax.servlet.ServletException</exception-type >
<location>/servletErr</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/errorpages/error403.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type >
<location>/errorpages/error403.jsp</location>
</error-page>
...
86
87
web.xml : Filtres
web.xml : Filtres
...
<filter>
<filter-name>LogFilter</filter-name>
<display-name>LogFilter</display-name>
<description>envoie des logs</description>
<filter-class>myapp.filters.LogFilter</filter-class>
</filter>
API javax.servlet.Filter
Permet de consulter et transformer les en-têtes et le
contenu des requêtes et des réponses
Traitement s’interposant entre le client et la ressource
requise (Servlet, JSP, fichier)
Chaînage de filtres selon l’ordre de déclaration dans
web.xml
Exemple de filtres
z
z
z
z
z
z
z
z
z
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/unPath/*</url-pattern>
</filter-mapping>
...
public class LogFilter implements javax.servlet.Filter {
private FilterConfig config;
public void init(FilterConfig fc) throws ServletException {
config = fc;
}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
config.getServletContext().log(
"Request received from: " + req.getRemoteHost());
chain.doFilter(req, res);
}
public void destroy() { }
}
88
web.xml : Accès à des paramètres
...
Context ctx = new InitialContext();
String value = (String) ctx.lookup("java:/comp/env/nom");
...
Filtre
http://localhost:8080/appli/unPath
Authentification, blocage des adresses IP
Conversion (Image), Transformation (XSLT)
Chiffrage, Compression
Log, ...
...
<env-entry>
<env-entry-name>nom</env-entry-name>
<env-entry-value>toto</env-entry-value>
<env-entry-type>java.lang.String</env-entry-type>
</env-entry>
...
Web.xml
89
web.xml : Autres fonctionnalités
z
Web.xml
Web.xml permet également de paramétrer :
z
z
Servlet
z
z
Centralisation de paramètres dans le fichier web.xml
accessibles aux servlets de l'application
z Illustration : paramètres d'accès à une base de données
z
z
z
z
90
Observateurs de session : réagir face à des
événements intervenants dans la session
Mapping de types MIME : lorsque des fichiers sont
envoyés au client, Tomcat génère automatiquement
un en-tête "Content-Type" pour les types définis
dans le descriteur
Références d'EJB
Configuration des sources de données (appels
JNDI)
Configuration de la JSP et des tags JSP (à voir)
Contraintes de sécurité (à voir)
etc.
91