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