framework MVC pour applications Web Servlets – JSP © Philippe GENOUD UJF Février 2010 1 Rappel Architecture MVC Serveur Internet (Serveur HTTP + Serveur Servlet/JSP) Serveur SGBD Requête HTTP Controleur Controleur (Servlet) (Servlet) 1 Modèle Modèle (Java (JavaBean) Bean) 2 Transfert Réponse HTTP Vue Vue (JSP) (JSP) Construit Modifie 3 Driver JDBC Consulte Bonne séparation des différents composants d’une application Web Modularité – réutilisation – évolutivité Séparation des compétences © Philippe GENOUD UJF Février 2010 2 Frameworks Web mais mettre en œuvre une "bonne" architecture MVC reste un tâche complexe surtout si plusieurs applications doivent profiter de cette architecture nombreux frameworks pour faciliter le développement d'applications Web C'est quoi un framework ? Dans le monde Java : Struts (Apache) Java Server Faces (SUN) Spring MVC Tapestry (Apache) Stripes Wicket (Apache) … © Philippe GENOUD mais aussi dans de nombreux autres langages : Symfony (PHP) Ruby On Rails (Ruby) Django (Python) Grails (Groovy) … UJF Février 2010 3 Framework Framework (cadre d'application) "ensemble de bibliothèques et de conventions permettant le développement rapide d'applications. Il fournit suffisamment de briques logicielles et impose suffisamment de rigueur pour pouvoir produire une application aboutie et facile à maintenir. Ces composants sont organisés pour être utilisés en interaction les uns avec les autres" http://www.techno-science.net/ Différence framework / bibliothèque (librairie) avec une librairie : exécution est dirigée par le code écrit par le développeur qui fait appel aux classes de la librairie avec un framework : exécution est dirigée par le code du framework qui fait appel au code écrit par le développeur le développeur doit compléter les classes et ressources préexistantes dans le framework © Philippe GENOUD UJF Février 2010 4 Struts c'est quoi ? Serveur Internet (Serveur HTTP + Serveur Servlet/JSP) Serveur SGBD Requête HTTP Controleur Controleur (Servlet) (Servlet) 1 Modèle Modèle (Java (JavaBean) Bean) 2 Transfert Réponse HTTP Vue Vue (JSP) (JSP) Construit Modifie 3 Driver JDBC Consulte Struts : Framework open source (fondation apache) pour le développement d'applications Web Java respectant le modèle MVC Architecture générique pour la partie contrôleur Facilités pour la réalisation des vues © Philippe GENOUD UJF Février 2010 5 Struts c'est quoi ? Le framework Struts encourage les architectures basées sur l'approche Model 2 (une variante du modèle classique MVC appliquée aux applications web). Le cœur du framework Struts est une couche contrôleur basée sur les technologies les plus acceptées Servlet/JSP, JavaBeans, ResourceBundles, XML. Struts fournit son propre composant contrôleur Struts intègre d'autres technologies pour offrir le Modèle et la Vue. Pour le Modèle, Struts peut interagir avec toutes les techniques d'accès aux données comme JDBC, EJB (Entreprise JavaBeans), Hibernate… Pour la Vue, Struts n'est pas limité aux JSP, il peut fonctionner aussi avec les Velocity Templates, le XSLT et d'autres systèmes de présentation. © Philippe GENOUD UJF Février 2010 6 Exemple d'application Struts © Philippe GENOUD UJF Février 2010 7 Documentation site de Struts http://struts.apache.org/ Struts 2.x réécriture du framework struts Struts 1.x dernière version : 1.3.10 version intégrée à NetBeans (6.7): 1.3.8 – http://struts.apache.org/1.3.8 au 2/02/1020 http://struts.apache.org/1.3.8/apidocs/index.html guide utilisateur © Philippe GENOUD UJF Février 2010 8 Architecture générale de Struts Fichier xml mapping vers les actions et les vues Regroupent les paramètres de la requête, qu'ils peuvent valider traitement la requête ActionForm ActionForm struts-config.xml struts strutsstruts-config.xml ActionForm1 ActionForm1 ActionForm2 ActionForm2 consulte La requête est transmise au contrôleur 1 2 ActionServlet ActionServlet Action1 Action1 (étend javax.servlet. http.HttpServlet) 4 Action2 Action2 Contrôleur 3 Choix et paramétrage de la vue selon la réponse de l'action et l’état du contrôleur Vue Vue (JSP) (JSP) © Philippe GENOUD Serveur SGBD Action Action Controleur générique « package» et aiguille le trafic HTTP vers le gestionnaire approprié Font le lien entre le contrôleur et la logique métier Modèle Modèle (Java (JavaBean) Bean) Création modification des objets modèle UJF Février 2010 9 Architecture générale de Struts Génération de la page HTML 5 6 consulte Envoi au client de la présentation issue de la vue Struts propose des bibliothèques de Tags pour faciliter la construction des pages JSP Modèle Modèle (Java (JavaBean) Bean) page page.jsp .jsp JSTL JSTL properties.en properties.en Struts Struts html, logic,beans html, logic,beans properties.fr properties.fr …biblio …biblioperso perso Ressources Bibliothèques de tags Fichiers de ressources pour messages utilisés dans les vues Vue © Philippe GENOUD UJF Février 2010 10 Struts Struts par par l'exemple: l'exemple: l’application l’application histogramme histogramme de de notes notes serveur Web Histogramme des notes image gif © Philippe GENOUD Serveur SGBD Page HTML avec fré fréquences des notes dans un tableau UJF Février 2010 11 Création d'un projet Struts Netbeans offre un support pour Struts (version 1.2.9) Fichiers de configuration struts Fichier de ressources pour les messages Librairies struts © Philippe GENOUD UJF Février 2010 12 Configuration Configuration :: web.xml web.xml Définition du contrôleur de l'application dans le fichier web.xml Le contrôleur est une servlet générique ActionServlet ou sous classe d'ActionServlet. <web-app <web-app version="2.5" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"...> xmlns="http://java.sun.com/xml/ns/javaee"...> <servlet> <servlet> <servlet-name>action</servlet-name> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <init-param> <param-name>config</param-name> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> </init-param> ... ... </servlet> </servlet> <servlet-mapping> <servlet-mapping> <servlet-name>action</servlet-name> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> <url-pattern>*.do</url-pattern> </servlet-mapping> </servlet-mapping> ... ... </web-app> </web-app> La localisation du fichier de configuration struts-config.xml Toutes les URL se terminant par le suffixe ".do" sont orientées vers ce contrôleur Possibilité de définir plusieurs contrôleurs dans une application struts © Philippe GENOUD UJF Février 2010 13 Configuration Configuration :: struts-config.xml struts-config.xml Une action est un traitement obtenu suite au passage d'une requête au contrôleur Struts Les actions sont décrites dans le fichier struts-configs dans la section <action-mappings>…</action-mappings> Au moyen de la balise <action> différents attributs de la balise <action> sont à renseigner selon que la requête et avec ou sans paramètres name : nom d'un objet ActionForm pour la récupération/validation des paramètres, défini dans la section <form-beans>…</form-beans> du fichier struts-config.xml la requête nécessite un traitement ou une simple redirection path : le path (relatif au contexte de l'application) auquel est associée l'action type : la classe de l'objet Action qui effectue le traitement associé à la requête forward : url de redirection © Philippe GENOUD UJF Février 2010 14 Simple redirection Action correspondant à une requête sans paramètres nécessitant une simple redirection accueil.html formulaireAnneeMatiere.jsp formulaireAnneeMatiere.do ActionServlet ActionServlet .jsp struts-config.xml <struts-config> <struts-config> ... ... <action-mappings> <action-mappings> définit le nom de l’URL correspondant à l'action (suffixe « .do » implicite) <action path="/formulaireAnneeMatiere" forward="/formulaireAnneeMatiere.jsp"/> </action-mappings> </action-mappings> ... ... </struts-config> </struts-config> © Philippe GENOUD l’URL à laquelle est relayée la demande UJF Février 2010 15 Simple Redirection assistant NetBeans pour ajout d'une action pour une requête sans paramètres nécessitant une simple redirection 2 1 Clic bouton droit 3 © Philippe GENOUD UJF Février 2010 16 Action avec traitement Action correspondant à une requête avec paramètres nécessitant un traitement notesAnneeMatiere.do tableau.jsp formulaireAnneeMatiere.jsp HistogramImager (servlet) © Philippe GENOUD UJF Février 2010 17 Action avec traitement ActionServlet ActionServlet notesAnneeMatiere.do? annee=2005& matiere=BD& présentation=tableau Action Action ActionForm ActionForm NotesAnneeMatiere NotesAnneeMatiere NotesAnneeMatiereForm NotesAnneeMatiereForm struts-config.xml struts strutsstruts-config.xml Instancie et initialise setAnnee(…), setMatiere(…) … validate(…) formulaireAnneeNote.jsp ActionErrors .jsp Si erreur sinon Consulte getAnnee(…), getMatiere(…) execute(…) Histogramme Histogramme Initialise le modèle tableau.jsp .jsp HistogramImager ActionForward struts-config.xml struts strutsstruts-config.xml Selon l'ActionForward © Philippe GENOUD UJF Février 2010 18 ActionForm JavaBean qui permet de stocker les propriétés des formulaires Hérite de org.apache.struts.action.ActionForm vérifie la validité des propriétés par sa méthode validate ActionForm ActionForm NotesAnneeMatiereForm NotesAnneeMatiereForm ActionErrors validate(ActionMapping, HttpServletRequest) ActionMapping : objet image de la configuration de l’action en cours stockée dans struts-config.xml HttpServletRequest : requête du client transmise par la Servlet de contrôle ActionErrors : permet de retourner des messages erreurs au client La classe dispose également d’autres méthodes ActionServlet getServlet() : retourne la Servlet qui gère le contrôle reset(ActionMapping, HttpServletRequest) : initialise les propriétés © Philippe GENOUD UJF Février 2010 19 ActionForm ActionForm ActionForm NotesAnneeMatiereForm NotesAnneeMatiereForm public class NotesAnneeMatiereForm extends ActionForm { public class NotesAnneeMatiereForm extends ActionForm { private private String String annee; annee; private String presentation; private String presentation; private private String String matiere; matiere; propriété public public void void setAnnee(String setAnnee(String annee) annee) {{ this.annee = annee; this.annee = annee; }} public public String String getAnnee() getAnnee() {{ return return annee; annee; } } modifieur et accesseur pour la propriété Chaque paramètre du formulaire est défini comme une propriété de l'objet ActionForm associé qui doit être un JavaBean public void setMatiere(String matiere) { public void setMatiere(String matiere) { this.matiere this.matiere == matiere; matiere; } } ... ... }} © Philippe GENOUD UJF Février 2010 20 ActionForm public public class class NotesAnneeMatiereForm NotesAnneeMatiereForm extends extends ActionForm ActionForm {{ private private String String annee; annee; private String presentation; private String presentation; private private String String matiere; matiere; ... ... Redéfinition (overriding) de la méthode validate ActionForm ActionForm NotesAnneeMatiereForm NotesAnneeMatiereForm @Override @Override public public ActionErrors ActionErrors validate(ActionMapping validate(ActionMapping mapping, mapping, HttpServletRequest request) { HttpServletRequest request) { ActionErrors errors = new ActionErrors(); ActionErrors errors = new ActionErrors(); Création d'un objet erreur vide if if (annee (annee == == null null || || annee.length() annee.length() << 1) 1) {{ errors.add("Année", new ActionMessage("error.annee.required")); errors.add("Année", new ActionMessage("error.annee.required")); }} else { else { Selon les paramètres ajout de try { try { messages d'erreur numeroAnnee = Integer.parseInt(annee); numeroAnnee = Integer.parseInt(annee); }} catch catch (NumberFormatException (NumberFormatException e) e) {{ errors.add("Année", errors.add("Année", new new ActionMessage("error.annee.isNotANumber")); ActionMessage("error.annee.isNotANumber")); }} }} }} }} if if ((!matiere.equals("graphic")) ((!matiere.equals("graphic")) && && ((!matiere.equals("tableau")) ((!matiere.equals("tableau")) {{ errors.add("Année", new ActionMessage("error.annee.required")); errors.add("Année", new ActionMessage("error.annee.required")); }} Le message est défini dans un fichier .properties return errors; Renvoie des erreurs (vide si la validation a réussi) return errors; © Philippe GENOUD UJF Février 2010 21 ActionForm les messages d'erreurs sont définis dans le fichier properties de l'application errors.add("Année", new ActionMessage("error.annee.isNotANumber")); pl2/notes/ApplicationResource.properties errors.header=<UL> errors.header=<UL> errors.prefix=<LI><span errors.prefix=<LI><span style="color: style="color: red"> red"> errors.suffix=</span></LI> errors.suffix=</span></LI> ... ... error.annee.required=Indiquez error.annee.required=Indiquez l'année l'année error.annee.isNotANumber=L'année doit être un nombre error.annee.isNotANumber=L'année doit être un nombre ... ... formulaireAnneeMatiere.jsp <%@ <%@ taglib taglib uri="http://struts.apache.org/tags-html" uri="http://struts.apache.org/tags-html" prefix="html" prefix="html" %> %> ... ... <html> <html> <body> <body> <H1>Histogramme <H1>Histogramme des des notes</H1> notes</H1> <HR> <HR> <form action="notesAnneeMatiere.do" method="post"> <form action="notesAnneeMatiere.do" method="post"> … … </form> </form> <HR> Tag de bibliothèque html <HR> <html:errors/> de Struts pour afficher les <html:errors/> </body> messages d'erreur </body> </html> </html> © Philippe GENOUD UJF Février 2010 22 ActionForm l'utilisation du fichier properties facilite la localisation (i18n) de l'application pl2/notes/ApplicationResource.properties un fichier ressource par langue errors.header=<UL> errors.header=<UL> errors.prefix=<LI><span style="color: red"> errors.prefix=<LI><span style="color: red"> pl2/notes/ApplicationResource.properties.en errors.suffix=</span></LI> errors.suffix=</span></LI> ... errors.header=<UL> ... errors.header=<UL> error.annee.required=Indiquez l'année errors.prefix=<LI><span style="color: red"> error.annee.required=Indiquez l'année errors.prefix=<LI><span style="color: red"> error.annee.isNotANumber=L'année nombre errors.suffix=</span></LI> error.annee.isNotANumber=L'année doit doit être être un un nombre errors.suffix=</span></LI> ... ... ... ... formHisto.title=Histogramme error.annee.required=Year required formHisto.title=Histogramme de de notes notes error.annee.required=Year required error.annee.isNotANumber=Year error.annee.isNotANumber=Year is is not not aa number number ... ... formHisto.title=Marks Histogram formHisto.title=Marks Histogram formulaireAnneeMatiere.jsp <%@ <%@ taglib taglib uri="http://struts.apache.org/tags-html" uri="http://struts.apache.org/tags-html" prefix="html" prefix="html" %> %> ... <%@ taglib uri="http://struts.apache.org/" prefix="bean" %> ... <html> <html> <body> <body> <H1> <bean:message Histogramme key="formHisto.title"/> des notes </H1> <H1> Histogramme des notes </H1> <HR> <HR> <form <form action="notesAnneeMatiere.do" action="notesAnneeMatiere.do" method="post"> method="post"> …… </form> </form> <HR> <HR> <html:errors/> <html:errors/> </body> </body> </html> </html> © Philippe GENOUD UJF Février 2010 23 ActionForm Les ActionForms doivent être déclarées dans le fichier struts-config.xml balise <form-bean> dans la section <form-beans>…</form-beans> <struts-config> <struts-config> Nom pour identifier l'action form <form-beans> <form-beans> <form-bean <form-bean name="NotesAnneeMatiereForm" name="NotesAnneeMatiereForm" type="pl2.notes.struts.forms.NotesAnneeMatiereForm"/> type="pl2.notes.struts.forms.NotesAnneeMatiereForm"/> ... ... </form-beans> </form-beans> Nom de la classe Java ... ... <struts-config> <struts-config> © Philippe GENOUD UJF Février 2010 24 ActionForm assistant NetBeans pour créer une ActionForm 1 nom de la classe 2 3 © Philippe GENOUD UJF Février 2010 25 Action Action Action Permet d'associer un traitement à une requête Hérite de org.apache.struts.action.Action Effectue le traitement par sa méthode execute ActionForward execute(ActionMapping, ActionForm,HttpServletRequest, HttpServletResponse) ActionMapping : objet image de la configuration de l’action en cours stockée dans struts-config.xml ActionForm : JavaBean qui stocke l’information du formulaire HttpServletRequest : référence de la requête HttpServletResponse : référence de la réponse ActionForward :objet identifiant la destination que le contrôleur (l' ActionServlet) doit choisir © Philippe GENOUD UJF NotesAnneeMatiere NotesAnneeMatiere Février 2010 26 Action Action Action public class NotesAnneeMatiere extends Action { private final static String HISTO_TABLE = "histotableau"; private final static String HISTO_GRAPHIC = "histographique"; NotesAnneeMatiere NotesAnneeMatiere public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { NotesAnneeMatiereActionForm f = (NotesAnneeMatiereForm) form; Récupération de l'objet ActionForm grâce au paramètre form INotesDAO notesDAO = DAOFactory().getNoteDAO(); Histogramme histo = notesDAO.getHistogramme(f.getNumeroAnnee(), f.getMatiere() ); request.setAttribute("notes", histo); if (f.getPresentation().equals("graphic")) { return mapping.findForward(HISTO_GRAPHIC); } else { return mapping.findForward(HISTO_TABLE); } Construction du modèle Le modèle est transmis via la requête Choix de la prochaine redirection } } © Philippe GENOUD UJF Février 2010 27 Action Declaration de l'Action dans le fichier struts-config.xml <struts-config> <struts-config> <form-beans> <form-beans> <form-bean <form-bean name="NotesAnneeMatiereActionForm" name="NotesAnneeMatiereActionForm" type="pl2.notes.struts.forms.NotesAnneeMatiereActionForm"/> type="pl2.notes.struts.forms.NotesAnneeMatiereActionForm"/> </form-beans> </form-beans> <action-mappings> <action-mappings> <action forward="/formulaireAnneeMatiere.jsp" path="/formulaireAnneeMatiere"/> <action forward="/formulaireAnneeMatiere.jsp" path="/formulaireAnneeMatiere"/> URL correspondant à l'action <action <action path="/notesAnneeMatiere" path="/notesAnneeMatiere" (suffixe « .do » implicite) type="pl2.notes.struts.actions.NotesAnneeMatiere" type="pl2.notes.struts.actions.NotesAnneeMatiere" name="NotesAnneeMatiereForm" Classe de l'action name="NotesAnneeMatiereForm" input="/formulaireAnneeMatiere.jsp" input="/formulaireAnneeMatiere.jsp" Nom de l'ActionForm scope="request" > scope="request" > associée <forward name="histotableau" path="/tableau.jsp"/> <forward name="histotableau" path="/tableau.jsp"/> Vue vers laquelle le <forward name="histographique" path="/histographic"/> <forward name="histographique" path="/histographic"/> contrôleur redirige en cas </action> </action> d'echec de la validation </action-mappings> </action-mappings> web.xml Etiquette de redirection if (f.getPresentation().equals("graphic")) { return mapping.findForward("histotagraphique"); } else { return mapping.findForward("histotableau"); } © Philippe GENOUD Chemin de redirection tableau.jsp .jsp UJF HistogramImager <servlet> <servlet-name>graphic</servlet-name> <servlet-class> pl2.notes.servlets.HistogramImager </servlet-class> </servlet> <servlet-mapping> <servlet-name>graphic</servlet-name> <url-pattern>/histographic</url-pattern> </servlet-mapping> Février 2010 28 Action assistant NetBeans pour créer une Action 1 2 3 © Philippe GENOUD UJF Février 2010 29 Action assistant NetBeans pour créer une Action suite Clic bouton droit 3 1 2 © Philippe GENOUD UJF Février 2010 30 Exceptions Action Action public class NotesAnneeMatiere extends Action { NotesAnneeMatiere NotesAnneeMatiere private final static String HISTO_TABLE = "histotableau"; private final static String HISTO_GRAPHIC = "histographique"; public ActionForward execute(ActionMapping mapping, ActionForm form, l'exception est relancée HttpServletRequest request, HttpServletResponse response) throws Exception { NotesAnneeMatiereActionForm f = (NotesAnneeMatiereForm) form; Que se passe-t'il si une DAOException est levée ? INotesDAO notesDAO = DAOFactory().getNoteDAO(); Histogramme histo = notesDAO.getHistogramme(f.getNumeroAnnee(), f.getMatiere() ); request.setAttribute("notes", histo); if (f.getPresentation().equals("graphic")) { return mapping.findForward(HISTO_GRAPHIC); } else { return mapping.findForward(HISTO_TABLE); } } } © Philippe GENOUD UJF Février 2010 31 Exceptions Si l'exception est relancée et qu'aucun traitement n'est mis en place pour celleci, une ServletException est créée par le contrôleur , chaînée avec l'exception originale et relancée, Servlet Exception Cause mère © Philippe GENOUD UJF Février 2010 32 Exceptions Possibilité de mettre en oeuvre un traitement spécifique des exceptions en redirigeant l'application vers une page spécifique struts-config.xml clé pour message d'erreur dans le fichier ressources la ressource vers laquelle l'application est redirigée le type de l'exception concernée pl2/notes/ApplicationResource.properties erreur_1.jsp © Philippe GENOUD UJF Février 2010 33 Exceptions Possibilité de mettre en oeuvre un traitement spécifique des exceptions en exécutant un gestionnaire d'erreur : une classe héritant de org.apache.struts.actions.ExceptionHandler redéfinition de la méthode execute la redirection s'effectue à l'aide d'un élément forward défini dans le fichier struts-config.xml La redirection peut être vers une action ou une page jsp struts-config.xml le lien entre un gestionnaire d'erreur et un type d'erreur est effectué dans le fichier struts-config.xml le type de l'exception concernée le gestionnaire d'exceptions invoqué © Philippe GENOUD UJF Février 2010 34 Exceptions Une exception est traitée par le gestionnaire d'exception le plus spécifique Exceptions de n'importe quel autre type Exceptions de type DAOException (classe DAOException ou n'importe quelle sous classe de DAOException) © Philippe GENOUD UJF Février 2010 35 Exceptions Possibilité de rédéfinir un gestionnaire spécifique au niveau d'une action gestionnaire général pour les DAOException gestionnaire spécifique pour les DAOException levées par l'action notesAnneeMatière © Philippe GENOUD UJF Février 2010 36 Exceptions assistant NetBeans pour définir un gestionnaire d'exceptions struts-config.xml Clic bouton droit 2 1 3 © Philippe GENOUD UJF Février 2010 37 Struts TagLibs Struts propose des bibliothèques de Tags Personnalisés qui aident les développeurs d'applications basées sur des formulaires Struts propose 4 bibliothèques de tag HTML Tags pour création d'interface utilisateur HTML, en particulier pour créer des formulaires de saisie Logic Tags pour la génération conditionnelle de texte, génération répétitive de texte en itérant sur des collections d'objets, gestion du flux de contrôle de l'application Bean Tags pour la définition de nouveaux objets JavaBeans dans différentes portées (application, session, requête…) et à partir de différentes sources Tags pour afficher un bean (ou une proriété d'un bean) sur la réponse de sortie. Nested Tags qui étendent les tags de base de Struts pour leur mise en relation lors d'imbrication © Philippe GENOUD UJF Février 2010 38 Struts TagLibs Exemple d'utilisation des tags HTML formulaireAnnneeMatiere.jsp Il faudrait intégrer dans la page de la logique permettant de réaffecter les éléments du formulaire avec les valeurs qui avaient été transmises Problème : les différents éléments du formulaire reprennent leur valeur initiale En cas de saisie incorrecte on revient sur cette page qui affiche alors les messages d'erreur © Philippe GENOUD UJF Février 2010 39 Struts TagLibs Exemple d'utilisation des tags HTML formulaireAnnneeMatiere.jsp Les tags HTML de Sruts prennent en charge l'initialisation des éléments du formulaire en cas de retour © Philippe GENOUD UJF Février 2010 40