«Anatomie» d une application web Servlets - JDBC Patrick REIGNIER - Philippe GENOUDUJF Février 2005 1 L application histogramme de notes HistoServlet HistoServlet HistogramModel HistogramModel (objet (objet métier) métier) Serveur SGBD Driver JDBC Patrick REIGNIER - Philippe GENOUDUJF Février 2005 2 1
Controleur * Réponse à une requête de type <CODE>get</CODE> * @param request données associées à la requête HTTP * @param response données associées à la réponse **/ public void doget(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { // Analyse de la requête HTTP // Récupération des données // Génération de la présentation Patrick REIGNIER - Philippe GENOUDUJF Février 2005 3 Controleur : analyse de la requête * Réponse à une requête de type <CODE>get</CODE> * @param request données associées à la requête HTTP * @param response données associées à la réponse **/ public void doget(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { // Analyse de la requête HTTP // Construction du modèle // Génération de la présentation int annee = 0 ; String matiere = null ; String presentation = null ; Attention de ne pas l oublier! annee = Integer.parseInt(request.getParameter("annee")) ; matiere = request.getparameter("matiere") ; presentation = request.getparameter("presentation") ; catch(numberformatexception e) { // ce cas ne devrait jamais arriver. Le test si // l'année est un nombre devrait être confié à javascript // côté client avec blocage de l'envoie du formulaire // si saisie incorrecte response.senderror(httpservletresponse.sc_internal_server_error, "L'année demandée n'est pas un nombre") ; return ; Patrick REIGNIER - Philippe GENOUDUJF Février 2005 4 2
Controleur : construction du modèle * Réponse à une requête de type <CODE>get</CODE> * @param request données associées à la requête HTTP * @param response données associées à la réponse **/ public void doget(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { // Analyse de la requête HTTP // Construction du modèle // Génération de la présentation HistogramModel notes = new HistogramModel(annee,matiere) ; notes.extrairenotes() ; catch (SQLException e) { response.senderror(httpservletresponse.sc_internal_server_error, e.getmessage()) ; return; Patrick REIGNIER - Philippe GENOUDUJF Février 2005 5 Controleur : génération du résultat * Réponse à une requête de type <CODE>get</CODE> * @param request données associées à la requête HTTP * @param response données associées à la réponse **/ public void doget(httpservletrequest request, HttpServletResponse response) throws IOException, ServletException { // Analyse de la requête HTTP // Construction du modèle // Génération de la présentation if (presentation.equals("tableau")) { // on génère la représentation de type tableau HTML response.setcontenttype("text/html"); out = response.getwriter(); out.println("<html><head><title>"); out.println("histogramme de notes"); out.println("</title></head><body>"); //Display Data out.println("<h1>bonus Calculation</H1>"); out.println("<p>soc Sec: " + socsec + "<P>"); out.println("<p>multiplier: " + multiplier + "<P>"); out.println("<p>bonus Amount: " + calc + "<P>"); out.println("</body></html>"); out.close(); else { // on génère la représentation de type image GIF Patrick REIGNIER - Philippe GENOUDUJF Février 2005 6 3
Modèle : construction Calcul de l histogramme des notes à partir de la BD => Requête SQL + analyse du résultat public HistogramModel(String matierere, int annee) throws SQLException { stmt = conn.createstatement() ; result = stmt.executequery("select note, count(*) from " + matiere + " where annee = " + annee + " group by note") ; // parcours du résultat de la requete et pour chaque note // on repporte son nombre d'occurences dans le tableau freq while (result.next()) { note = new Integer(result.getString(1)).intValue() ; freq[note] = new Integer(result.getString(2)).intValue() ; rs.close(); stmt.close(); Problème : - Quand se connecter? - Quand se déconnecter? Patrick REIGNIER - Philippe GENOUDUJF Février 2005 7 connexion à la BD 1ère solution : au niveau du modèle public HistogramModel(String matierere, int annee) throws SQLException { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3036/notes",, ); stmt = conn.createstatement() ; result = stmt.executequery("select note, count(*) from " + matiere + " where annee = " + annee + " group by note") ; // parcours du résultat de la requete et pour chaque note // on repporte son nombre d'occurences dans le tableau freq while (result.next()) { note = new Integer(result.getString(1)).intValue() ; freq[note] = new Integer(result.getString(2)).intValue() ; stmt.close(); conn.close(); Connexion / déconnexion à chaque requête Opération coûteuse Approche peu efficace Paramètres de la BD en «dur» dans le modèle Patrick REIGNIER - Philippe GENOUDUJF Février 2005 8 4
connexion à la BD 2ème solution : au niveau du contrôleur public class HistoServlet extends HttpServlet { public void doget() { public void destroy() { conn.close(); catch (SQLException e) { Écriture message fichier log protected Connexion conn; public void init() throws ServletException { Class.forName("com.mysql.jdvc.Driver").newInstance(); conn = DriverManager.getConnection("jdbc:mysql://localhost:3036/notes",, ); catch (Exception e) { throw new ServletException("problème lors du chargement du driver", e); Patrick REIGNIER - Philippe GENOUDUJF Février 2005 9 connexion à la BD 2ème solution : au niveau du contrôleur Piège : Servlets multithreadés : la même instance sert toutes les requêtes objet connexion partagé par toutes les requêtes => la base de données ne voit qu une transaction plutôt qu une transaction par thread de requêtes risque d incohérence si entrelacement des requêtes Patrick REIGNIER - Philippe GENOUDUJF Février 2005 10 5
Rappel : Cycle de vie des Servlets Moteur de servlets Créer un pool de threabd Thread Thread Thread Instancier la servlet Servlet Appbeler init() initialisation Requête HTTP 1 Affecter une requête à un thread Appeler service() Requête HTTP 2 Affecter une requête à un thread Appeler service() Exécuter le service Réponse HTTP 1 Exécuter le service Réponse HTTP 2 Terminer le pool de threads Appeler destroy() Patrick REIGNIER - Philippe GENOUDUJF Février 2005 11 Connexion à la BD 2ème solution bis : au niveau du contrôleur éviter le multithreading public class HistoServlet extends HttpServlet implements SingleThreadModel { protected Connexion conn; Une instance de servlet par requête public void init() throws ServletException { Class.forName("com.mysql.jdvc.Driver").newInstance(); conn = DriverManager.getConnection("jdbc:mysql://localhost:3036/notes",, ); catch (Exception e) { throw new ServletException("problème lors du chargement du driver", e); Grand nombre de connexions vers la base Risque de dégradation des performances Risque de rejet par la base Mauvais passage à l échelle Connexions inactives Risque de timeout au niveau de la base la servlet correspondante ne fonctionne plus Patrick REIGNIER - Philippe GENOUDUJF Février 2005 12 6
connexion à la BD Utilisation d une liste de connexions disponibles Lors de l accès à la base de données : Demande d une connexion disponible à la liste Accès à la BD à travers cette connexion Restitution de la connexion à la liste Au niveau de la liste : Création de nouvelles connexions si nécessaire Fermeture des connexions inactives depuis trop longtemps Connection pool = spécification J2EE (javax.sql) Patrick REIGNIER - Philippe GENOUDUJF Février 2005 13 connexion à la BD javax.sql.datasource = liste de connexions Ressource partagée par toutes les sessions (Objet global) Mise à disposition par un serveur d objets : JNDI (Java Naming Directory Interface) Désignation de la ressource dans le fichier de déploiement (web.xml) <servlet-mapping> </servlet-mapping> <resource-ref> <res-ref-name>jdbc/notes</res-ref-name> <res-type>javax.sql.datasource</res-type> <res-auth>container</res-auth> </resource-ref> Patrick REIGNIER - Philippe GENOUDUJF Février 2005 14 7
connexion à la BD Définition de la ressource dans le fichier de configuration du serveur (conf/server.xml) <Context docbase="notes" path="/notes" reloadable="true"> <Logger classname="org.apache.catalina.logger.filelogger" prefix="localhost_notes_log." suffix=".txt" timestamp="true"/> <Resource name="jdbc/notes" auth="container" type="javax.sql.datasource"/> <ResourceParams name="jdbc/myoracle"> <parameter> <name>driverclassname</name> <value>oracle.jdbc.driver.oracledriver</value> </parameter> <parameter> <name>url</name> <value>jdbc:oracle:thin:@hoff.imag.fr:1521:ufrima</value> </parameter> <parameter> <name>username</name> <value>genoud</value> </parameter> <parameter> <name>password</name> <value>bd2003</value> </parameter> </ResourceParams> </ResourceParams> </Context> Voir la documentation dans $CATALINA_HOME/webapps/tomcat-docs/jndi-datasource-examples-howto.html Patrick REIGNIER - Philippe GENOUDUJF Février 2005 15 connexion à la BD Utilisation de la source de données dans le code java public class Controleur extends HttpServlet { * la source de données qui permettra d'accéder à la base de donnée * (c'est elle qui gère un pool de connexions, et qui fournira les * connexions aux différents objets en ayant l'usage). */ private DataSource datasource; Création de la source de données dans le servlet contrôleur * A l'initialisation du servlet, création d'un pool de connexions * qui servira à fournir des connexions à la base de données * utilisés par les composants modèle. */ public void init() throws ServletException { Context init = new InitialContext(); Context ctx = (Context) init.lookup("java:comp/env"); datasource = (DataSource) ctx.lookup("jdbc/notes"); catch (NamingException ne) { throw new ServletException("problème lors du chargement du driver ", ne); Patrick REIGNIER - Philippe GENOUDUJF Février 2005 16 8
connexion à la BD Utilisation de la source de données dans le code java Dans le modèle public HistogramModel(String matierere, int annee ),DataSource ds) throws SQLException // obtention d une connexion Connexion Connection conn conn = ds.getconnection(); = // depuis la source de données DriverManager.getConnection("jdbc:mysql://localhost:3036/notes" (le pool de connexions),, ); stmt = conn.createstatement() ; result = stmt.executequery("select note, count(*) from " + matiere + " where annee = " + annee + " group by note") ; // parcours du résultat de la requete et pour chaque note // on repporte son nombre d'occurences dans le tableau freq while (result.next()) { note = new Integer(result.getString(1)).intValue() ; freq[note] = new Integer(result.getString(2)).intValue() ; stmt.close(); conn.close(); // libère la connexion (elle est remise dans le pool) Patrick REIGNIER - Philippe GENOUDUJF Février 2005 17 connexion à la BD Très peu de changement dans le code Plus de référence explicite dans le code Au driver jdbc A l url d accès à la base Au nom de l utilisateur A son mot de passe Possibilité de changer de base lors du déploiement sans avoir besoin de recompiler l application Patrick REIGNIER - Philippe GENOUDUJF Février 2005 18 9
Structure de déploiement des applications web Répertoire d installation des applications web A chaque application web correspond un répertoire dont le contenu respecte une structuration normalisée à partir de la spécification 2.2 des servlets Classes java (et ressources associées) nécessaires à l application Si organisation en packages cela doit être traduit dans la hiérarchie des sous répertoires de classes Les.jar des bibliothèques non standard de classes utilisées par l application (Attention!! pas de.zip) Descripteur de déploiement de l application Pages et tous les fichiers accessibles par client du serveur web ( *.html, *.jsp, *.css, images, etc) Patrick REIGNIER - Philippe GENOUDUJF Février 2005 19 Fichiers.jar visibles pour toutes les applications web + code interne de Tomcat JDBC 2.0 Optional Package APIs, définit entre autres javax.sql.datasource. JavaMail 1.2 APIs, afin d écrire des applications «mail-enabled». Servlet 2.3 et JSP 1.2 APIs indispensables pour l écriture de servlets et pages JSP. Fichiers.jar visibles pour toutes les applications web Patrick REIGNIER - Philippe GENOUDUJF Février 2005 20 10