Struts 2. Le framework de développement d'applications Java EE. Résumé. Jérôme LAFOSSE. ENI Editions - All rigths reserved

Dimension: px
Commencer à balayer dès la page:

Download "Struts 2. Le framework de développement d'applications Java EE. Résumé. Jérôme LAFOSSE. ENI Editions - All rigths reserved"

Transcription

1

2 Struts 2 Le framework de développement d'applications Java EE Jérôme LAFOSSE Résumé Ce livre sur Struts 2 s adresse aux développeurs Java qui souhaitent disposer d un ouvrage de référence pour mettre en application le framework Java EE le plus répandu. L ouvrage est décomposé en vingt-trois chapitres qui expliquent le fonctionnement et la mise en place de projets web à partir du framework. Les premiers chapitres décrivent le framework Java EE de référence avec ses services et son installation au travers du modèle de conception MVC. Les chapitres 3 et 4 présentent un exemple concret de projet Struts 2 avec la gestion des traces et débogage. Dans le chapitre suivant, le lecteur apprendra en détail la gestion des actions, le mapping, les formulaires et les redirections. Le chapitre 6 présente de manière exhaustive la bibliothèque de tags Struts. Dans la suite du livre, le lecteur appréhendera la gestion des messages et l internationalisation ainsi que les validations des entrées, les types et conversions. Un chapitre est consacré à la couche modèle d accès aux données, à l upload et download de données et au chargement des pages. Le développement d intercepteurs est longuement détaillé dans le chapitre 14 ainsi que la gestion des résultats dans le chapitre 15. Les chapitres suivants sont consacrés à Ajax Struts et aux moteurs de templates ainsi qu à l affichage des informations au travers d XSLT. Les derniers chapitres concernent l utilisation et le développement de plug-ins avec Struts ainsi que la configuration zéro et le langage OGNL. Chaque concept est abordé de façon théorique et technique afin de permettre aux concepteurs disposant de connaissances en Java EE d utiliser une API facilitant les développements d applications web. Les applications utilisées dans les chapitres sont issues d exemples concrets et sont téléchargeables sur cette page et sur la plate-forme de l auteur L'auteur Ingénieur en informatique et diplômé du CNAM, Jérôme Lafosse intervient comme consultant, concepteur et formateur sur les technologies Java. Spécialiste des technologies web, il travaille à promouvoir les outils et solutions Open Source pour le développement de projets Internet. Il enseigne également la plate-forme Java Entreprise Edition et la conception de projets Web en Licence et Master.Il est l'auteur du livre Java EE - Guide de développement d'applications web en Java dans la collection Epsilon aux Editions ENI. Ce livre numérique a été conçu et est diffusé dans le respect des droits d auteur. Toutes les marques citées ont été déposées par leur éditeur respectif. La loi du 11 Mars 1957 n autorisant aux termes des alinéas 2 et 3 de l article 41, d une part, que les copies ou reproductions strictement réservées à l usage privé du copiste et non destinées à une utilisation collective, et, d autre part, que les analyses et les courtes citations dans un but d exemple et d illustration, toute représentation ou reproduction intégrale, ou partielle, faite sans le consentement de l auteur ou de ses ayants droit ou ayant cause, est illicite (alinéa 1er de l article 40). Cette représentation ou reproduction, par quelque procédé que ce soit, constituerait donc une contrefaçon sanctionnée par les articles 425 et suivants du Code Pénal. Copyright Editions ENI - 1 -

3 Qu est ce qu un framework? Il existe en programmation deux types d individus : Les programmeurs système. Les programmeurs d applications. Les programmeurs système écrivent le code qui sera utilisé par les programmeurs d applications. Les programmeurs système développent les langages Java, PHP, C ou encore C++ et les programmeurs d applications utilisent ces langages et outils pour créer de la valeur ajoutée à des fins commerciales. Les programmeurs d applications se concentrent sur leurs projets sans se soucier des techniques et mécaniques de bas niveaux. Les programmeurs d applications utilisent des bibliothèques ou outils appelés : framework. Un framework est un ensemble de bibliothèques, d outils et de règles à suivre, qui aident au développement d applications. Les frameworks sont développés par des programmeurs système. Un framework est composé de plusieurs briques/composants qui sont en interaction les uns avec les autres. Les applications peuvent être écrites de manière plus efficace si nous utilisons un framework adapté au projet au lieu d être obligé de réinventer à chaque fois la roue. Un framework Java fournit un ensemble de fonctionnalités à partir d une implémentation objet. Lors de développement à grande échelle et de conception par équipe, les frameworks sont alors très utiles, voire indispensables. Actuellement, différents types de frameworks sont disponibles : les frameworks d infrastructure système, qui permettent de développer des systèmes d exploitation, des outils graphiques et des plates formes web (Struts, Spring...) ; les frameworks communicants (appelés intergiciels) ; les frameworks d entreprise (développements spécifiques) ; les frameworks de gestion de contenu (type Content Management System). Les frameworks permettent la réutilisation de code, la standardisation du développement et l utilisation du cycle de développement de type itératif incrémental (spécification, codage, maintenance et évolution). On parle également parfois de progiciel évolué lorsque l on désigne un framework et son cycle de vie. Actuellement, il existe beaucoup de frameworks dans tous les domaines d application et avec pratiquement tous les langages. Voici une liste non exhaustive des frameworks utilisés en Java : Apache Struts WebWork JSF (Java Server Faces) Spring Wicket - 1 -

4 Pourquoi utiliser un framework? Les Servlets ont été définies en 1998 et deux ans après, de grandes entreprises avaient déjà misé sur Java pour leurs applications web. Pendant plusieurs années, ces entreprises ont développé leurs projets de façon autonome sans standard. Aujourd hui, toutes ces sociétés mesurent l importance des frameworks. Le choix du framework de développement est stratégique pour une entreprise, il sera déterminant pour la qualité, la productivité et la pérennité des projets. 1. Normes et standards Le développement informatique avec l utilisation de normes permet de généraliser les bonnes pratiques et d harmoniser les développements au sein de l entreprise, ce qui facilite la maintenance. La plate forme de développement Java EE permet d utiliser des normes, mais également des outils complexes eux mêmes accompagnés de normes. 2. Framework et développement web La définition initiale de l API Servlet est trop faible pour envisager un développement complexe d applications totalement basées sur des Servlets. Au départ, les applications Java étaient basées sur le principe de l API Common Gateway Interface (CGI) et progressivement les frameworks Java sont apparus pour combler les manques ou faiblesses de l API Servlet et JavaServer Pages (JSP). Le choix de l API aura un impact non négligeable sur les performances, la réalisation, la qualité et la maintenance de l application. De même, puisque le framework sera le socle de base sur lequel le logiciel sera construit, sa pérennité sera elle même fondamentale

5 Les différents frameworks Il existe plusieurs types d outils pour le développement d applications. Un framework de type maison, c est à dire développé par l entreprise, n est pas la meilleure solution. Dès les premières années de Java, les équipes d informaticiens ont inventé leurs propres outils pour le développement et de grandes entreprises ont parfois construit leur propre framework. Ces développements sont à éviter, car aucune entreprise ne pourra consacrer suffisamment d efforts nécessaires pour la maintenance et l évolution du framework. De plus, les frameworks OpenSource deviennent des standards et sont testés, validés à une échelle mondiale par l intermédiaire des projets réalisés. Les frameworks d éditeur présentent un risque pour les entreprises d un point de vue développement. En effet, ils possèdent toujours un objectif caché qui est la fidélisation de l entreprise sur les outils de l éditeur. Les frameworks OpenSource sont actuellement les plus nombreux et les plus aboutis. Nous retrouvons ici la qualité du travail et la même dynamique que le projet Apache. Une bonne part des projets de frameworks est d ailleurs issue du consortium Apache. Les frameworks sont des outils complexes quel que soit la qualité de développement et l origine des projets. Il n est pas nécessaire de maîtriser tous les frameworks existants, mais ceux ci doivent être utilisés correctement. Une fois le framework choisi, il est alors nécessaire de se former et de constituer une cellule d assistance aux équipes de développement

6 Quel framework choisir? Le développement Internet basé sur la technologie Java a été submergé par des API et outils de toutes sortes. Le choix d un framework est basé sur différents critères : Est ce que nous devons tout concevoir de A à Z? Est ce que le développement permet l utilisation d une application précédemment développée ou une partie? Est ce que nous pouvons utiliser un environnement comme fondement de l application? La conception de A à Z permet de parfaitement maîtriser une technologie mais nécessite beaucoup de temps et d argent. Le développement à partir d applications existantes est intéressant uniquement si les développeurs des projets antérieurs sont présents. La troisième approche (utiliser un environnement comme fondement de l application) est sans aucun doute la meilleure dans la plupart des cas

7 Introduction à la programmation Java Entreprise Edition Les technologies Servlets et JavaServer Pages (JSP) sont le socle de base du développement Java EE. Le problème avec ces technologies est la quantité de code à développer pour les communications HTML/JSP, Servlets et Modèles. De même, sans l utilisation d un modèle de conception de type Modèle Vue Contrôleur (MVC), le mélange de scripts HTML, SQL et Java est une mauvaise idée. Le débogage est alors plus complexe et plus long à réaliser. Le mélange de code empêche la réutilisation et visibilité des structures de contrôle. Affichage et accès aux données étant confondus. Enfin, l utilisation d objets JavaBeans et de gestionnaires de balises comme JSTL (Java Standard Tag Library) permet un développement simple et consistant, même pour des projets complexes. L écriture de code HTML/XHTML dans des pages JSP est très rapide pour le développeur. En effet, les JSP ne remplacent pas les Servlets mais sont largement complémentaires. Avec un modèle de site très simple entièrement réalisé en JSP, les pages sont compilées et transformées en Servlets. C est le modèle le plus utilisé par les programmeurs débutants car il est simple et rapide à mettre en œuvre. Avec un modèle de conception un peu plus complexe, les développeurs utilisent les Servlets pour les traitements, et les pages JSP pour l affichage. Ce modèle est plus difficile à mettre en œuvre mais il est plus naturel et offre une meilleure maintenance. Enfin, pour une architecture multiniveaux de type MVC, les Servlets représentent l aspect Contrôle, les Modèles l accès aux données et les JSP la partie Vue. Ce modèle est plus complexe à développer mais beaucoup plus simple à tester, à maintenir et faire évoluer. De même, la notion de réutilisabilité est mise en avant avec ce type de modèle

8 Struts 1 Le projet OpenSource Jakarta Struts développé par le consortium Apache permet d accélérer le développement d applications Internet. Struts 1 est quasiment devenu le standard de fait pour les projets Java EE. Struts 1 est un environnement agréable et puissant qui gère l application ainsi que les tâches courantes (routage, actions, validations...). Un autre avantage de son utilisation est le nombre croissant d utilisateurs qui tendent à pérenniser le projet. Actuellement beaucoup d environnements de développement comme Eclipse proposent des outils pour la programmation Struts 1. Struts 1 est un framework qui offre des outils de validation des entrées utilisateurs (saisies et formulaires), des bibliothèques de balises JSP pour la création rapide de pages, une technique de routage pour les pages et accès web et un processus de création de formulaires à base de fichiers XML. Struts 1 offre également d autres avantages : Struts 1 fonctionne avec tous les serveurs Java EE (Tomcat, WebSphere, Weblogic...). Struts 1 propose une architecture solide et stable (projet Apache). Struts 1 est adapté aux applications web de grande taille. Struts 1 permet de décomposer une application complexe en composants plus simples. Struts 1 garantit un développement similaire par les équipes de programmeurs. Struts 1 possède une documentation abondante. Struts 1 permet un développement rapide et peu onéreux. Le terme Struts fait référence aux piliers ou étais, dans le sens architectural du terme, avec la notion de morceaux ou briques qui soutiennent des bâtiments, des maisons et des ponts afin d éviter qu ils ne deviennent des ruines

9 Struts 2 Comme nous venons de le préciser dans l introduction, le modèle de conception de type MVC est actuellement préconisé pour le développement d applications web évoluées. Cependant, les principaux inconvénients de ce type de conception sont : la difficulté à comprendre le modèle et le niveau d expertise que cela requiert ; le nombre de fichiers à produire (environ trois fois plus) ; l aspect rébarbatif des tâches à réaliser. La question est alors de savoir comment diminuer ces inconvénients et augmenter la productivité. Craig R. McClanahan s décide peu avant 2000 de créer un outil de conception de type framework pour accélérer les développements Java EE. Il fait ensuite don à la fondation Apache en mai 2000 de son framework nommé Struts 1.0 et sa première sortie est programmée pour juin Il devient alors le framework de conception Java EE le plus populaire du monde. À peu près à la même période, plusieurs développeurs travaillent à la création d un autre framework de développement nommé WebWork. Ce framework n aura jamais la popularité de Struts mais reste supérieur sur plusieurs points, notamment la mise en place de validations de formulaires et l architecture globale pour la gestion des JavaBeans (pas besoin d utiliser des Beans de formulaires). Un point important de WebWork par rapport à Struts 1.X est la notion de tests. Avec Struts 1.X un navigateur web est nécessaire pour réaliser une partie des tests mais WebWork peut fonctionner sans. Fin 2005, le produit WebWork et le framework le plus populaire Struts 1.0 fusionnent pour fonder Struts TI (Titanium) qui devient rapidement Struts 2.0. Les concepteurs de Struts 1.0 et Jason Carreira s responsable de WebWork, proposent alors un framework regroupant les avantages des deux précédents outils (WebWork et Struts 1.0). Cependant, Struts 2 n est pas une extension de Struts 1 et cela peut malheureusement décevoir plusieurs développeurs et architectes web car ce nouveau framework est une refonte complète. Les spécialistes devront ainsi réapprendre en intégralité les commandes et fonctionnalités de Struts 2.0. La refonte correspond en effet plus à WebWork version 2.2 (WebWork étant lui même basé sur XWork d Open Symphony) qu à une évolution de Struts 1.0. Le framework Struts 2 repose sur une déclaration de l architecture sous forme de fichiers XML ou avec des annotations Java localisées dans les fichiers des classes d actions. Struts 2 est un framework orienté actions. Les actions sont décomposées en trois rôles. Premièrement, les actions jouent le rôle le plus important du framework en encapsulant le traitement et le travail à réaliser par le service. Deuxièmement, les actions permettent de manipuler automatiquement les données des requêtes lors des transferts. Troisièmement, le framework détermine quel résultat doit être retourné et la vue à afficher en réponse à un traitement. Les actions Struts 2 implémentent des objets JavaBeans (classes Java simples) pour chaque groupe de données envoyées dans la requête. Chaque paramètre de la requête est déclaré dans la classe d action avec un nom identique pour réaliser automatiquement le mapping des valeurs. La finalité d une action étant de retourner une chaîne de caractères permettant de sélectionner le résultat à afficher. Pour résumer, Struts 2 repose donc sur le modèle de conception de type MVC II comme il est expliqué dans le schéma suivant. Il permet un développement plus rapide, plus souple et résout plusieurs problèmes de conception en fournissant les services suivants : un système évolué de gestion du routage ou navigation ; un système de validation de formulaires et d entrées, simple à mettre en œuvre ; un système puissant de plug ins ou d extensions (pour les graphiques, sources de données ) ; la gestion de l internationalisation pour le développement de sites multilingues ; le support de la technologie Ajax ; un outil de débogage en standard ; une bibliothèque puissante de balises

10 Architecture MVC II Struts 2 propose (tout comme Struts 1 d ailleurs) d utiliser un minimum de règles de conceptions qui ne sont malgré tout pas obligatoires : Ne pas utiliser de code 100 % Java dans les pages JSP. Toute la logique de contrôle étant placée dans les classes d actions (Servlets). Utiliser des bibliothèques de balises pour accéder aux objets et parcourir des collections. Écrire le minimum de code répétitif et utiliser les outils proposés par le framework. Ce framework est ainsi conçu pour aider les développeurs d applications web en Java à créer des projets de qualité selon une norme ou standard. Ce framework aide également les développeurs à organiser la logique de l application. Le choix du framework Struts 2 repose sur les points suivants : Fiabilité : le projet est développé et suivi depuis mai Ce projet jouit d une excellente réputation et améliore sans cesse ses défauts. Flexibilité : chaque action peut être personnalisée, les fichiers de configuration sont très souples en terme d utilisation et les validations sont simples à mettre en œuvre. Performance : l architecture conçue par Struts 2 repose sur WebWork. Elle est particulièrement performante et maintenable grâce à la séparation par couche. Les principales caractéristiques du framework Struts 2 sont les suivantes : les types de conversions automatiques pour les collections issues des requêtes HTTP ; les fichiers de configuration modulables utilisés par paquetages ; les annotations Java 5 qui réduisent les lignes de code pour la configuration ; l utilisation de tags permet d appliquer des thèmes ou modèles (templates) ; l utilisation du langage d expression, OGNL ; la mise en place optionnelle du plug in intercepteur (interceptor) permettant d exécuter des requêtes complexes et longues en tâche de fond avec la soumission multiple (rafraîchissement de pages) ; l intégration simple d outils comme JSTL, Spring ou Hibernate. Le framework Struts 2 est une seconde génération de framework MVC. Le principal avantage de la notion d intercepteurs est la flexibilité de l ensemble et la configuration proposée. Struts 2 repose également sur le principe d empaquetage des actions. Lorsque nous déclarons des classes d action avec un fichier XML ou des annotations Java, le framework organise tous ces composants sous la forme d une logique de paquetage (packages). Les paquetages Struts 2 sont similaires aux paquetages Java. Ce mécanisme permet également de grouper les actions par domaine. Les URLs de l application sont alors associées à des paquetages où chaque action est déclarée

11 Installation du framework Struts 2 Le framework Struts 2 est basé sur Java EE 5 (Servlets 2.4 et JSP 2.0 minimum). Pour utiliser les exemples du livre, nous utilisons les dernières versions des outils avec le JDK 1.6, Tomcat 6.X, les Servlets 2.5, Struts et Eclipse/Lomboz 3.4. Le site officiel de Struts 2 est accessible à cette adresse : L installation de l API est simple, il suffit de copier les fichiers téléchargés dans les répertoires d une application web Java EE traditionnelle. Nous pourrons créer un nouveau projet et copier les fichiers nécessaires (librairies.jar) ou alors installer le framework en utilisant une application Struts vierge livrée avec le framework ( apps.zip). Il existe plusieurs versions de projets pour installer Struts 2. Dans la partie Download du site, nous retrouvons les dernières versions de Struts avec des archives au format.zip. Les versions struts version all.zip incluent toutes les librairies, les fichiers sources et des exemples de mise en application. La version utilisée dans ce guide est struts all.zip de 90 Mo. La version struts version lib.zip contient uniquement les librairies au format.jar nécessaires à la mise en place de Struts 2. Le framework Struts V2 est composé de plusieurs fichiers. Les librairies Java (.jar) contiennent toutes les classes utilisées par le framework : commons fileupload.jar (librairie de gestion de l upload en Java). commons logging.jar (librairie de loggin/traces). commons io version.jar (librairie de gestion des entrées/sorties). freemarker version.jar (librairie utilisée pour la mise en page et le moteur de templates). ognl version.jar (librairie utilisée pour la manipulation d objets Java). junit version.jar (librairie du framework de gestion des tests unitaires). struts2 core version.jar (librairie complète Struts 2, c est la bibliothèque principale). xwork version.jar (librairie de XWork avec les dépendances). La bibliothèque native du framework est struts2 core version.jar tandis que les librairies commons, sont fournies par la fondation Apache et le projet Commons project. Il existe deux façons d installer le framework Struts : Copier les librairies.jar dans le répertoire /WEB INF/lib d une nouvelle application. Utiliser une application Struts vide, livrée en standard permettant d installer le framework. Cette application vide porte le nom de struts2 blank version.war pour indiquer qu elle est vierge. Pour installer cette application, il est nécessaire de : Copier l archive struts blank version.war dans un répertoire (ex : installationstruts2). Décompresser son contenu. Ouvrir Eclipse et cliquer sur Fichier Nouveau Projet Java Projet Tomcat ou Projet web. Nommer le projet (ex : installationstruts2) et sélectionner le répertoire précédent

12 Projet Eclipse Éditer le projet et renommer les paquetages sans le terme java. L arborescence du projet doit alors être la suivante : Arborescence installationstruts2 Démarrer Tomcat et ouvrir un navigateur. Saisir l URL suivante dans le navigateur : Nous pouvons également tester l application d authentification fournie en standard :

13 Formulaire installationstruts2-3 -

14 Présentation Struts propose un système de plug ins pour augmenter les fonctionnalités du framework. Un plug in Struts est une archive au format.jar composée de classes Java, de pages d affichage Velocity ou FreeMarker et d un fichier strutsplugin.xml. Les plug ins Struts sont livrés sous la forme de fichiers.jar déposés dans le répertoire /WEB INF/lib de l application. Dans l archive du plug in un fichier struts plugin.xml permet de définir le paquetage et ses résultats. Pour rappel, Struts charge les fichiers de gestion dans cet ordre : Le fichier struts default.xml présent dans la librairie struts2 core 2.x.jar. Tous les fichiers nommés struts plugin.xml déployés dans l application. Le fichier principal de l application, struts.xml. Chaque plug in Struts peut utiliser et déclarer de nouveaux paquetages, types de résultats, intercepteurs, actions ou librairies de balises. De très nombreux plug ins plus ou moins utiles sont disponibles sur Internet et peuvent être utilisés avec le framework

15 Le plug in JFreeChart JFreeChart ( est une librairie Java OpenSource permettant de créer des graphiques évolués. La librairie téléchargeable sur le site est composée des archives suivantes qui doivent être copiées dans le répertoire /WEB INF/lib de l application pour utilisation : jfreechart x.jar : la librairie graphique JFreeChart. jcommon x.jar : la librairie dépendante pour la création des graphiques. struts2 jfreechart plugin 2.x.jar : le plug in JFreeChart pour Struts. La librairie struts2 jfreechart plugin 2.x.jar est nécessaire pour la gestion des graphiques. En effet, cette librairie contient notamment le fichier struts plugin.xml définissant le résultat de type chart. De même, la version du plug in doit être identique à la version de Struts. La création d une application Struts JFreeChart repose sur les étapes suivantes : Création d un paquetage de type jfreechart default. Utilisation de la classe JFreeChart pour créer le graphique. Définition d un résultat de type chart et initialisation des paramètres width et height pour la taille du graphique. Définition d un attribut nommé chart dans la classe d action avec son getter afin de retourner l objet à afficher. Les graphiques sont retournés sous la forme d images au format PNG ou JPEG. L application exemple33 permet de saisir le partage du temps de travail du client informaticien (pourcentage du temps à faire de l analyse, du développement, des tests et de la maintenance) et d afficher un graphique à base de camembert (pie) pour représenter la répartition. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple33" namespace="/" extends="jfreechart-default"> <default-action-ref name="temps_client" /> </package> </struts> <action name="temps_client"> <result>/jsp/tempsclient.jsp</result> </action> <action name="graphique" class="exemple33.clientaction"> <result name="success" type="chart"> <param name="value">chart</param> <param name="type">png</param> <param name="width">600</param> <param name="height">400</param> </result> </action> Code : exemple33.clientaction.java - 1 -

16 package exemple33; import org.jfree.chart.chartfactory; import org.jfree.chart.jfreechart; import org.jfree.chart.plot.pieplot3d; import org.jfree.data.general.defaultpiedataset; import org.jfree.util.rotation; import com.opensymphony.xwork2.actionsupport; import exemple33.javabeans.client; public class ClientAction extends ActionSupport // objet pour le graphique private JFreeChart chart; // objet client private Client client; public Client getclient() return client; public void setclient(client client) this.client = client; // générer le graphique à partir des informations public String execute() throws Exception // créer la liste des données à afficher dans le graphique DefaultPieDataset donnees=new DefaultPieDataset(); donnees.setvalue("analyse", this.client.getpourcentageanalyse()); donnees.setvalue("développement", this.client.getpourcentagedeveloppement()); donnees.setvalue("tests", this.client.getpourcentagetest()); donnees.setvalue("maintenance", this.client.getpourcentagemaintenance()); titre // créer le graphique chart = ChartFactory.createPieChart3D( "Répartition du temps de travail du client", // ); donnees, true, true, true // données à afficher // afficher la légende // créer le camembert ieplot3d plot=(pieplot3d) chart.getplot(); // angle d affichage plot.setstartangle(190); // rotation plot.setdirection(rotation.clockwise); // transparence du schéma plot.setforegroundalpha(0.4f); plot.setnodatamessage("pas de données à afficher"); return SUCCESS; // getter pour retourner l image public JFreeChart getchart() return chart; - 2 -

17 Code : /jsp/tempsclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>répartition du temps client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>répartition du temps client</h3> <s:form method="post" action="graphique" id="formulaire_client" name="formulaire_client"> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:textfield name="client.pourcentageanalyse" id="client.pourcentageanalyse" label="pourcentage Analyse" labelposition="top" cssclass="input"/> <s:textfield name="client.pourcentagedeveloppement" id="client.pourcentagedeveloppement" label="pourcentage Developpement" labelposition="top" cssclass="input"/> <s:textfield name="client.pourcentagetest" id="client.pourcentagetest" label="pourcentage Tests" labelposition="top" cssclass="input"/> <s:textfield name="client.pourcentagemaintenance" id="client.pourcentagemaintenance" label="pourcentage Maintenance" labelposition="top" cssclass="input"/> <s:submit value="valider" id="boutonvalider"/> </s:form> </div> </body> </html> - 3 -

18 Arborescence du projet exemple33-4 -

19 Formulaire de saisie des valeurs client - 5 -

20 Affichage du graphique JFreeChart Nous pouvons également insérer directement un graphique dans une image d un document XHTML à l aide de la notation suivante : <img src="graphique.action"/>. Le nouvel exemple33ajax permet d afficher dynamiquement des graphiques de façon aléatoire à l aide de la balise XHML <img/> et de code JavaScript. Code : /jsp/tempsclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>répartition du temps client</title> <style type="text/css">@import url(css/styles.css);</style> <!-- fichier de gestion --> <script src="javascript/exemple33ajax.js" type="text/javascript"></script> </head> <body> <div id="enveloppe"> <img src="" id="image"/> <script language="javascript"> // action effectuée suite au chargement window.load=affichergraphique(); </script> </div> </body> </html> Code : /javascript/exemple33ajax.js // fonction qui permet l affichage du graphique en Ajax function affichergraphique() // lien de l action avec un paramètre aléatoire pour forcer le rafraîchissement de l image et éviter le cache var action="graphique.action?"+new Date().getTime(); // insérer l image dans la balise var im=new Image(); im.src=action; document.images["image"].src=im.src; // changer l affichage dans 2 secondes settimeout("affichergraphique()",2000); - 6 -

21 Le plug in Tiles Le plug in Tiles permet de gérer l apparence et le découpage des applications web. Les projets Internet utilisent habituellement les découpages sous forme d en tête, de menu, de contenu et de pied de page. Pour réaliser ce découpage, les développeurs utilisent des tableaux HTML ou mieux, des définitions à base de feuilles de styles CSS et de balises <span/>, <div/> ou autres. Le projet comprend alors les pages fragments appelées par exemple entete.jspf, menu.jspf et piedpage.jspf. Ensuite, chaque vue du site utilise des inclusions dynamiques à partir de la directive JSP <%@ include file=... %> ou du tag <jsp:include/>. Le projet exemple34 basé sur l application exemple08 utilise un découpage du site à partir de fragments de pages JSP et de la balise <%@ include file=... />. Code : /jspf/entete.jspf <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>gestion des clients</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <div id="entete">gestion des clients</div> <div id="menu"> MENU<br/> <a href="/exemple34">afficher le formulaire</a> </div> <div id="enveloppe"> Code : /jsp/ajouterclient.jsp <%@ include file="../jspf/entete.jspf" %> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" id="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter un client"/> </s:form> <%@ include file="../jspf/piedpage.jspf" %> Code : /jsp/afficherclient.jsp <%@ include file="../jspf/entete.jspf" %> <p> <h4><s:property value="%gettext( client.afficher )"/></h4> <s:property value="%gettext( client.identifiant )"/>: <s:property value="identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/>: <s:property value="motdepasse"/><br/> </p> <%@ include file="../jspf/piedpage.jspf" %> Code : /jspf/piedpage.jspf </div> <div id="piedpage">projet exemple34 utilisation des fragments</div> </body> </html> - 1 -

22 Arborescence du projet exemple34 Affichage du formulaire client à l aide de fragments Cette technique très utilisée dans la plupart des applications web est très vite limitée pour des projets de grande envergure. En effet, si la présentation change (le nom des fragments) ou la mise en page, nous devons modifier chaque page utilisatrice. Pour éviter cela, le plug in Tiles fournit une librairie de tags permettant de définir la mise en page pour toutes les pages utilisatrices du site. Un changement dans la définition des pages entraîne une modification instantanée de toutes les pages utilisatrices. Pour cela, Tiles repose sur une définition de pages au format JSP ou XML permettant de préciser la structure du site. Tiles est un composant développé à l origine pour Struts 1. Désormais, au vue de sa popularité, Tiles est devenu un projet Apache à part entière Le plug in Struts Tiles est livré en standard avec les librairies Struts struts 2.1.X lib.zip (tiles core 2.X.jar, tiles api 2.X.jar, tiles jsp 2.x.jar et struts2 tiles plugin 2.1.X.jar). Afin de faire fonctionner correctement le plug in Tiles avec Struts 2, les librairies dépendantes suivantes ont été également installées : commons beanutils 1.X.jar, commons collections 3.X.jar et commons digester 1.X.jar. Le plug in Tiles repose sur deux composants, le modèle et la définition. Le modèle des pages est défini à partir d une page JSP. Chaque page JSP utilisée par le modèle de découpage sera déclarée dans la page. Maintenant, si nous voulons changer tout le modèle de présentation de l application, il suffit de changer une seule page JSP de définition pour appliquer les changements. Afin de mettre en application ce plug in, nous allons définir un nouveau projet exemple35 à partir du précédent. 1. Page de mise en forme du modèle La page JSP ci dessous permet de définir le modèle de présentation du site. Nous retrouvons en début de fichier l inclusion de la librairie de tags et deux utilisations de balises : - 2 -

23 <tiles:getasstring name="titrepage"/> : permet de retourner une chaîne de caractères définit dans le fichier de configuration. <tiles:insertattribute name="entete"/> : permet d inclure une page dans la définition du modèle de présentation. La balise <tiles:/> possède un attribut role permettant de spécifier le rôle de l utilisateur nécessaire à l exécution de la page. Code : /jsp/modelepresentation.jsp <%@ taglib uri=" prefix="tiles"%> <html> <head> <title><tiles:getasstring name="titrepage"/></title> </head> <body> <tiles:insertattribute name="entete"/> <tiles:insertattribute name="contenu"/> <tiles:insertattribute name="piedpage"/> </body> </html> 2. Définition du modèle Une définition de modèle est utilisée entre la page de présentation du modèle et les pages JSP utilisatrices. Par analogie Java, la page de présentation du modèle (layout) est souvent comparée à une interface et la définition du modèle à la classe utilisatrice de l interface fournissant une implémentation de celle ci. Architecture Tiles La page de définition du modèle est précisée dans un fichier tiles.xml présent dans le répertoire /WEB INF de l application Struts. Le fichier tiles.xml du projet exemple35 est présenté ci dessous à l aide de la balise <definition/> et des attributs name et template. Code : /WEB-INF/tiles.xml <?xml version="1.0" encoding="iso "?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN" " <tiles-definitions> <definition name="ajouterclient" template="/jsp/modelepresentation.jsp"> <put-attribute name="titrepage" value="ajout d un client"/> <put-attribute name="entete" value="/jspf/entete.jspf"/> <put-attribute name="contenu" value="/jsp/ajouterclient.jsp"/> - 3 -

24 <put-attribute name="piedpage" value="/jspf/piedpage.jspf"/> </definition> <definition name="afficherclient" template="/jsp/modelepresentation.jsp"> <put-attribute name="titrepage" value="affichage du client"/> <put-attribute name="entete" value="/jspf/entete.jspf"/> <put-attribute name="contenu" value="/jsp/afficherclient.jsp"/> <put-attribute name="piedpage" value="/jspf/piedpage.jspf"/> </definition> </tiles-definitions> La balise <definition/> contient une ou plusieurs balises <put-attribute/> utilisées en référence à la page de présentation du modèle. Pour notre projet, la page de présentation utilisée par le modèle est /jsp/modelepresentation.jsp. La définition AjouterClient permet de déclarer une variable nommée titrepage associée à la présentation du modèle et trois pages afin d insérer respectivement l en tête, le contenu et le pied de page. Par la suite, dans le fichier de configuration de l application struts.xml, les résultats de type tiles seront associés aux différentes définitions du fichier tiles.xml (AjouterClient et AfficherClient). 3. Mise en place du plug in Tiles La mise en place du plug in Tiles est réalisée en quatre étapes : Copie des archives tiles core 2.X.jar, tiles api 2.X.jar, tiles jsp 2.x.jar et struts2 tiles plugin 2.1.X.jar dans le répertoire /WEB INF/lib de l application. Définition du listener associé à Tiles dans le fichier de configuration /WEB INF/web.xml. <listener> <listenerclass>org.apache.struts2.tiles.strutstileslistener</listenerclass> </listener> Définition des résultats de type tiles dans le fichier de configuration struts.xmi ou utilisation du paquetage tiles default. <result-types> <result-type name="tiles" class="org.apache.struts2.views.tiles.tilesresult"/> </result-types> Utilisation des résultats de types tiles dans les résultats des actions Struts. Le projet exemple35 est terminé, le fichier struts.xml contient la définition des actions et des résultats de type tiles. Ces derniers sont en rapport avec le fichier de définition du modèle ModelePresentation.jsp et les définitions du fichier tiles.xml. Les résultats définis dans le fichier struts.xml sont envoyés au modèle de présentation ModelePresentation.jsp et utilisent les données présentent dans les définitions AjouterClient et AfficherClient du fichier tiles.xml. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="utf-8"?> <web-app id="exemple35" version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <listener> <listenerclass>org.apache.struts2.tiles.strutstileslistener</listenerclass> - 4 -

25 </listener> <filter> <filter-name>struts2</filter-name> <filterclass>org.apache.struts2.dispatcher.ng.filter.strutsprepareandexec utefilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple35" namespace="/" extends="struts-default"> <result-types> <result-type name="tiles" class="org.apache.struts2.views.tiles.tilesresult"/> </result-types> <default-action-ref name="ajouter_client" /> <action name="ajouter_client" class="exemple35.clientaction"> <result type="tiles">ajouterclient</result> </action> <action name="validerajouter_client" class="exemple35.clientaction" method="ajouter"> <result name="input" type="tiles">ajouterclient</result> <result name="success" type="tiles">afficherclient</result> </action> </package> </struts> Code : /jsp/modelepresentation.jsp <%@ taglib uri=" prefix="tiles"%> <html> <head> <title><tiles:getasstring name="titrepage"/></title> </head> <body> <tiles:insertattribute name="entete"/> <tiles:insertattribute name="contenu"/> <tiles:insertattribute name="piedpage"/> </body> </html> - 5 -

26 Utilisation du plug in Tiles pour l architecture des pages - 6 -

27 Écrire un plug in L écriture d un plug in n est pas une tâche très complexe avec le framework Struts. Le principe est le même que la création d archives au format.jar. Nous allons écrire un plug in permettant de générer directement une collection d objets sous la forme d un flux RSS (reprise du projet exemple23). L arborescence de l application utilise une notation de paquetage pleinement qualifiée afin de pouvoir être exportée : Arborescence du projet exemple36 Le paquetage com.pluginstruts2.rss contient notre classe rssresult.java permettant de générer une collection d objets sous la forme de flux RSS. Cette classe utilise deux paramètres, collectionobjet pour préciser le nom de la collection à afficher et lien pour réaliser les liens de chaque item du flux rss. Code : /com/pluginstruts2/rss/rssresult.java package com.pluginstruts2.rss; import java.io.printwriter; import java.util.list; import javax.servlet.http.httpservletresponse; import org.apache.struts2.strutsstatics; import org.apache.struts2.dispatcher.strutsresultsupport; import public class rssresult extends StrutsResultSupport // récupérer le nom de l objet private String collectionobjet; // lien pour le fichier rss private String lien; public String getcollectionobjet() return collectionobjet; public void setcollectionobjet(string collectionobjet) this.collectionobjet = collectionobjet; public String getlien() return lien; public void setlien(string lien) this.lien = lien; - 1 -

28 // méthode exécutée par le résultat public void doexecute(string finallocation,actioninvocation invocation) throws Exception // récuperer l objet response HttpServletResponse response=(httpservletresponse)invocation.getinvocationcontext().ge t(strutsstatics.http_response); // récupérer la liste des objets dans la pile d exécution List<Object> listeobjects=(list<object>)invocation.getstack().findvalue(this.getcol lectionobjet()); // type de réponse response.setcontenttype("application/xml"); PrintWriter out=response.getwriter(); out.println("<?xml version=\"1.0\" encoding=\"utf- 8\"?>"); out.println("<rss version=\"2.0\">"); // créer un canal out.println("<channel>"); out.println("<title>flux RSS des objets</title>"); out.println("<link>"+this.getlien()+"</link>"); out.println("<description>flux RSS des objets</description>"); // créer les objets for(int i=0;i<listeobjects.size();i++) Object o=(object)listeobjects.get(i); out.println("<item>"); out.println("<title>"+o+"</title>"); out.println("<link>"+this.getlien() +"</link>"); out.println("<description>"+o+"</description>"); out.println("</item>"); out.flush(); out.close(); L étape de génération de l archive (.jar) consiste à créer un répertoire indépendant, à conserver l arborescence des paquetages, à déposer le fichier compilé.class et à créer le fichier struts plugin.xml à la racine du paquetage. Arborescence d un plug in Struts Le fichier struts plugin.xml commence par la définition du nom de paquetage qui sera utilisé par la suite dans les autres paquetages. Un résultat de type rss est précisé avec l association de la classe à exécuter pour ce type. Enfin, deux paramètres sont initialisés par défaut pour le nom de la collection et le lien de chaque item du flux rss. Code : struts-plugin.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" "

29 <struts> <package name="rss-default" extends="struts-default"> <result-types> <result-type name="rss" class="com.pluginstruts2.rss.rssresult" default="false"> <param name="collectionobjet">listeobjets</param> <param name="lien"> </result-type> </result-types> </package> </struts> La structure est complète, nous pouvons passer à la création de l archive avec l outil jar livré avec le jdk Java. Pour cela, il est nécessaire de se placer dans le répertoire du plug in et de lancer la commande suivante : jar -cvf plugin-struts2-rss.jar * Un nouveau paquetage nommé plugin struts2 rss.jar est alors créé à la racine de l arborescence du plug in. Génération d une archive pour le plug in Struts - 3 -

30 Utiliser le plug in Le plug in a été créé, nous pouvons passer à son utilisation en copiant la librairie plugin struts2 rss.jar dans le répertoire /WEB INF/lib d une application. Le plug in est utilisé dans le fichier de configuration struts.xml afin de déclarer un paquetage qui l utilise. Le fichier de configuration commence par la définition du paquetage qui utilise le plug in (extends="rss-default") et la définition du résultat de type rss avec les liens à réaliser dans le flux RSS et le nom de la collection d objets qui doit être affichée. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="false" /> <package name="exemple36" namespace="/" extends="rss-default"> <default-action-ref name="listerrss_client" /> <action name="listerrss_client" class="exemple36.clientaction" method="lister"> <result type="rss"> <param name="lien"> <param name="collectionobjet">listeclients</param> </result> </action> </package> </struts> Enfin, la classe d action ClientAction.action utilise la collection statique nommée listeclients et le plug in rss. L application est alors totalement fonctionnelle. Code : exemple36.clientaction.java package exemple36; import java.util.list; import com.opensymphony.xwork2.actionsupport; import exemple36.javabeans.client; import public class ClientAction extends ActionSupport private Client client; private List<Client> listeclients; public Object getmodel() return client; public Client getclient() return client; public void setclient(client client) this.client = client; public List<Client> getlisteclients() return listeclients; - 1 -

31 public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; // retourner la liste des clients après récupération public String lister() listeclients=clientmodele.getlisteclients(); return SUCCESS; Code : exemple36.javabeans.client.java package public class Client private int idclient; private String identifiant; private String motdepasse; public Client() public Client(int idclient,string identifiant, String motdepasse) this.idclient=idclient; this.identifiant=identifiant; this.motdepasse=motdepasse; // getter et setter public String tostring() String res="identifiant : "+this.getidentifiant()+" - Mot de passe : "+this.getmotdepasse(); return res; - 2 -

32 - 3 -

33 Les autres plug ins Le site propose un inventaire des différents plug ins disponibles pour le framework Struts. Nous retrouvons des plug ins pour Hibernate, Ajax, JavaScript, les images ou encore l utilisation facilitée de tableaux. La communauté des plug ins Struts est actuellement en période de développement et de nouveaux plug ins, plus ou moins utiles et fonctionnels, sont mis en ligne pratiquement chaque jour. À charge du développeur, de travailler avec parcimonie à l aide de ces plug ins

34 En résumé Le framework Struts propose une forme simple et efficace de création de plug ins. Ce chapitre a présenté le mécanisme de plug in Struts ainsi que l utilisation à partir des archives et du fichier de configuration struts.xml. Le plug in JFreeChart permet de créer des graphiques complexes à l aide d un résultat de type chart. Le plug in Tiles est utilisé pour l architecture des vues et afin de faciliter la maintenance de l ensemble. Enfin, les deux derniers paragraphes présentent le développement de plug ins personnalisés et l utilisation à partir de fichiers sources et de l archive au format.jar

35 Présentation Comme nous avons pu le voir depuis le début de cet ouvrage, la configuration des actions, validations ou résultats est une tâche simple avec Struts, réalisée à partir de fichiers XML. Le framework propose cependant, une seconde approche appelée configuration zéro ou zero configuration. Au lieu d utiliser le fichier struts.xml pour préciser les classes et le routage, les classes sont annotées

36 Configuration Si nous souhaitons utiliser la configuration zéro, c est à dire sans fichier xml, nous devons indiquer à Struts qu il doit utiliser un paquetage ou une liste de paquetages comme classes d action. La première version de Struts 2 proposait l utilisation du système Zero Config qui permettait de préciser le paquetage à annoter dans le fichier /WEB INF/web.xml. Cette technique a ensuite été améliorée au profit du plug in CodeBehind permettant la simplification des déclarations de paquetages à annoter et le fonctionnement global. Depuis la version 2.1 de Struts, ces deux techniques sont dépréciées au profit du plug in Convention. Le plug in Convention (struts2 convention plugin 2.x.jar) est livré en standard dans l archive struts2.x lib.rar et téléchargeable sur le site officiel de Struts 2. Cette archive contient les librairies standards ainsi que les plug ins qui peuvent être utilisés avec la version indiquée du framework. Ce plug in permet de réaliser des annotations d actions, d intercepteurs, de validations ou encore de type de conversions. Les principales fonctionnalités de ce plug in sont résumées ci après : Notation d actions. Convention de nommage pour les résultats. Convention de nommage pour les accès aux noms de classes par URL. Convention de nommage pour les paquetages. Notation d intercepteurs. Notation d espaces de nommages. Notation de paquetages XWork. Afin d utiliser le plug in Convention, il est nécessaire de copier la librairie struts2 convention plugin 2.x.jar dans le répertoire /WEB INF/lib de l application. Le fichier struts.xml de configuration d applications n est plus obligatoire, l application utilise automatiquement le plug in Convention si celui ci est installé et présent dans le classpath. Dans le cas d une distribution de l application sous la forme de paquetage avec la configuration zéro, il est nécessaire de positionner le paramètre struts.convention.action.disablejarscanning à true

37 Utilisation Par défaut, le plug in Convention utilise des résultats automatiques présents dans le répertoire /WEB INF/content de l application. Cette configuration peut être modifiée avec la propriété struts.convention.result.path présente dans le fichier de propriétés Struts. Le mapping des URLs est réalisé à l aide des noms, ainsi l action déclenchée avec l URL sera automatiquement associée à la vue JSP /WEB INF/content/client.jsp. 1. Nommage Pour mettre en œuvre la configuration zéro, nous allons utiliser un nouveau projet exemple37 à partir de l application exemple14. Par défaut, le plug in Convention cherche toutes les classes héritant de la classe com.opensymphony.xwork2.action ou qui sont suffixées par le mot Action dans des paquetages spécifiques. Les paquetages utilisés par le plug in Convention doivent respecter une convention de nommage et sont appelés struts, struts2, action ou actions. Chaque paquetage contenant un de ces noms, est considéré comme un paquetage du plug in Convention. Ensuite, le plug in regarde si les classes de ces sous paquetages héritent de la classe com.opensymphony.xwork2.action ou si elles sont suffixées par le terme Action. L appel des URLs repose ensuite sur l arborescence des paquetages : La classe exemple37.actions.client.java sera appelée par l URL /client et le namespace /. La classe exemple37.actions.pagination.client.java sera appelée par l URL /pagination/client et le namespace /pagination. Nous pouvons également indiquer au plug in d ignorer certains paquetages (afin de ne pas appliquer les actions) avec le paramètre struts.convention.exclude.packages dans le fichier de configuration struts.xml. Nous pouvons également à l inverse, préciser à Struts dans quel paquetage appliquer les actions avec le paramètre struts.convention.action.packages. 2. Annotations d actions L utilisation d annotations d actions permet de préciser l URL qui va déclencher l action pour l application. L permet de déclarer une URL et l permet de mapper de multiples URLs. La notation d URL est sensible à la casse mais le suffixe.action est automatiquement utilisé dans les URLs. Nous pouvons préciser autant d annotations d actions que la classe possède de fonctionnalités à exécuter. Le projet exemple37 permet de lister les clients à partir de la collection du modèle. La classe d action suivante permet de réaliser le traitement de l application à l aide d annotations d actions. Code : exemple37.actions.clientaction.java package exemple37.actions; import java.util.list; import org.apache.struts2.convention.annotation.action; import com.opensymphony.xwork2.actionsupport; import exemple37.javabeans.client; import public class ClientAction extends ActionSupport // liste des clients private List<Client> listeclients; // annotation du plug-in public String execute() System.out.println("Trace dans la méthode de liste"); listeclients=clientmodele.getlisteclients(); return SUCCESS; public List<Client> getlisteclients() - 1 -

38 return listeclients; public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; Arborescence du projet exemple37 Pour définir l action par défaut, le paramètre <default action ref name="listerclient" /> du fichier de configuration struts.xml peut être remplacé par une définition d action sous la forme d annotation. Dans notre projet, l action lancée par l URL courante (value="/") est la fonction Lister() de notre projet. // annotation du results=@result(name="success", location="/web- INF/content/ListerClient.jsp") results=@result(name="success", location="/ WEB-INF/content/ListerClient.jsp") ) ) public String lister() System.out.println("Trace dans la méthode de liste"); listeclients=clientmodele.getlisteclients(); return SUCCESS; 3. Annotations de résultats Par défaut, comme nous l avons précisé dans ce chapitre, le plug in Convention utilise le répertoire /WEB INF/content ainsi que chaque page associée à l action pour réaliser le routage. L propose deux types de résultats : globaux ou locaux. Les résultats globaux permettent de partager des résultats à toutes les méthodes de la classe d action. Tout comme les actions, les résultats sont définis sous la forme d annotations. À l inverse, les résultats locaux permettent de préciser des résultats action par action. Nous utilisons pour la suite un nouveau projet exemple38, adapté du projet exemple14 pour mettre en application le principe de la configuration zéro et la gestion des résultats

39 4. Annotations d intercepteurs Pour la gestion des clients nous utilisons l intercepteur paramsprepareparamsstack avec la déclaration suivante dans le fichier de configuration de l application struts.xml : <action name="editer_client" class="exemple14.clientaction" method="editer"> <interceptor-ref name="paramsprepareparamsstack"/> <result name="success">/jsp/editerclient.jsp</result> </action> La mise en place d intercepteur avec la configuration zéro est réalisée avec l et le nom de l intercepteur à utiliser avant la définition de la classe d action. Code public class ClientAction extends ActionSupport implements Preparable... L permet de déclarer plusieurs intercepteurs dans la même classe @InterceptorRef("validation") ) public class ClientAction extends ActionSupport implements Preparable... Les annotations d intercepteurs peuvent également être placées au niveau de chaque déclaration d action. Pour cela, nous utilisons le paramètre d action nommé interceptorrefs. De même, l attribut params de l est composé d un tableau de clés permettant de préciser la configuration de l intercepteur : cle1, valeurcle1, cle2, valeurcle2. Dans notre projet exemple38, nous devons utiliser, en plus du paramètre paramsprepareparamsstack, l intercepteur validation pour les vérifications de saisies du client dans les formulaires d ajout et d édition. Le tableau params contient également un ensemble de propriétés afin de préciser que les validations sont réalisées à l aide d un fichier XML sans déclaration dans la classe. interceptorrefs=@interceptorref(value="validation",params="progra mmatic", "true", "declarative", "false") Le projet exemple38 peut maintenant être adapté à partir du projet complet qui utilisait auparavant le fichier de configuration struts.xml. Code : exemple38.actions.clientaction package exemple38.actions; import java.util.list; import org.apache.struts2.convention.annotation.action; import org.apache.struts2.convention.annotation.actions; import org.apache.struts2.convention.annotation.interceptorref; import com.opensymphony.xwork2.actionsupport; import com.opensymphony.xwork2.preparable; import org.apache.struts2.convention.annotation.result; import exemple38.javabeans.client; public class ClientAction extends ActionSupport implements Preparable // liste des clients - 3 -

40 private List<Client> listeclients; // objet client private Client client; // client a modifier private int idclientencours; public void prepare() throws Exception // en création, créer un nouvel objet vide if(idclientencours==0) client=new Client(); // en modification, retourner les infos de l objet else client=clientmodele.getclient(idclientencours); // annotation du results=@result(name="success", location="/jsp/listerclient.jsp") results=@result(name="success", location="/jsp/listerclient.jsp") ) ) public String lister() listeclients=clientmodele.getlisteclients(); return SUCCESS; // annotation du plug-in location="/listerclient", location="/jsp/listerclient.jsp"), interceptorrefs=@interceptorref(value="validation",params="progra mmatic", "true", "declarative", "false") ) public String ajouter() ClientModele.ajouter(client); return SUCCESS; // afficher le formulaire en results=@result(name="success", location="/jsp/editerclient.jsp") ) public String editer() return SUCCESS; // modifier un results= - 4 -

41 @Result(name="success", location="/listerclient", location="/jsp/editerclient.jsp") ) public String modifier() ClientModele.modifier(client); return SUCCESS; // supprimer un client à partir du paramètre reçu nommé results=@result(name="success", location="/listerclient", type="redirect") ) public String supprimer() ClientModele.supprimer(idClientEnCours); return SUCCESS; public List<Client> getlisteclients() listeclients=clientmodele.getlisteclients(); return listeclients; public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; public Client getclient() return client; public void setclient(client client) this.client = client; public int getidclientencours() return idclientencours; public void setidclientencours(int idclientencours) this.idclientencours = idclientencours; - 5 -

42 Arborescence du projet exemple38 Gestion complète des clients à partir d annotations 5. Annotations d espace de nommage L annotation de namespace permet de changer la convention de nommage utilisée par défaut avec Struts. Lorsque cette annotation est placée au niveau de la classe d action, celle ci est appliquée pour toutes les méthodes de l action. Nous pouvons modifier notre projet précédent exemple38 en ajoutant un nouveau namespace pour accéder au projet par des URLs différentes. Code : exemple38.actions.clientaction package - 6 -

43 public class ClientAction extends ActionSupport implements Preparable... L application est désormais accessible à partir des URLs suivantes : Annotations des vues L permet de spécifier le chemin de stockage des résultats. Nous pouvons utiliser cette annotation avant la déclaration de la classe d action. Code : exemple38.actions.clientaction @InterceptorRef("paramsPrepareParamsStack") public class ClientAction extends ActionSupport implements Preparable Annotations des exceptions L est définie au niveau de la déclaration de la classe d action et contient des permettant de définir les types d exceptions à gérer. Code : exemple38.actions.clientaction = "java.lang.nullpointerexception", result = "success", params = "parametre1", public class ClientAction extends ActionSupport implements Preparable Chargement automatique des configurations Le plug in Convention peut être rechargé automatiquement sans redémarrer le contexte de l application à la façon du fichier de configuration struts.xml et de la directive <constant name="struts.devmode" value="true" />. Pour recharger automatiquement le plug in Convention, nous devons utiliser la constante suivante dans le fichier struts.xml de l application : <constant name="struts.convention.classes.reload" value="true" /> ou dans le fichier de propriétés

44 En résumé Ce chapitre a présenté le principe d un framework et l intérêt de l utilisation d un tel outil pour les développements web. La seconde partie introduit les différents framewoks et apporte des pistes de réflexion pour le choix d un framework. La partie suivante est un rappel des services proposés par le framework Struts 1 tandis que le nouvel outil Struts 2 est présenté ainsi que son installation et la mise en place d un premier exemple vierge

45 En résumé Le framework Struts propose une technique nommée configuration zéro ou zero configuration permettant de ne pas créer et écrire le fichier de configuration de l application struts.xml et d ainsi éviter de déclarer chaque action, résultat ou intercepteur. Cette technique utilise pour cela le plug in Convention livré en standard et téléchargeable depuis le site du framework. La mise en place de cette configuration zéro est basée sur les annotations Java 5 placées au début des classes ou méthodes des classes. Chaque paquetage de l application doit respecter une convention de nommage afin de bénéficier de la configuration par annotation simplifiée. Lorsque cette convention de nommage est respectée, les développeurs peuvent créer des annotations pour les actions, les résultats, les intercepteurs à utiliser, l espace de nommage de l application, les vues à afficher et la gestion des exceptions. Le projet exemple38 reprend en détail la mise en place de cette technique à partir des fonctionnalités majeures d une application Internet (lister, ajouter, modifier, supprimer)

46 Présentation Ce chapitre est consacré à la présentation détaillée des différents intercepteurs et au langage de manipulation et d expressions Java Object Graph Navigation Language OGNL. Ce langage de manipulation de données simplifie l accès aux informations des getters et setters des JavaBeans. De même, cette librairie peut être utilisée en association avec les taglibs JSTL

47 Intercepteurs Struts Les intercepteurs sont des filtres qui permettent de réaliser des tâches afin de simplifier et d améliorer le travail des développeurs. Dans la plupart des cas, les intercepteurs proposés en standard par Struts sont suffisants et il est essentiel de comprendre l intérêt et l apport de chacun d eux. Les intercepteurs proposés par défaut avec le framework sont présentés ci dessous. params Gestion du mapping entre les paramètres des requêtes et les propriétés des actions. staticparams Gestion des paramètres statiques déclarés dans les définitions et classes d action. prepare Gestion de l accès aux modèles de classes. scope Gestion du mécanisme de sessions. servletconfig Gestion de l accès aux classes HttpServletRequest et HttpServletResponse. validation Gestion des validations de formulaires. token Gestion du double envoi ou double submit. tokensession Gestion du double envoi ou double submit avec un paramètre de session. profiling Gestion du profilage des actions. timer Gestion du temps d exécution des actions. roles Gestion des rôles des Realms pour la sécurisation des applications. modeldriven Gestion des modèles de persistances. scopedmodeldriven Gestion des modèles de persistances à la manière de l intercepteur précédent mais avec gestion de session. logger Gestion des actions. execandwait - 1 -

48 Gestion des chargements de pages lors des traitements plus ou moins longs. debugging Gestion du débogage des applications. exception Gestion du traitement des exceptions. i18n Gestion de l internationalisation pour les applications multilingues. fileupload Gestion des chargements de fichiers. store Gestion des portées des messages de succès et d erreurs. chain Gestion du chaînage des actions et du passage de paramètres. createsession Gestion de la session utilisateur courant. conversionerror Gestion des erreurs de conversion dans les actions. alias Gestion des noms de paramètres dans les requêtes HTTP. checkbox Gestion des cases à cocher dans les formulaires. cookie Gestion du cookie utilisateur. workflow Gestion de l appel des méthodes de validations dans les classes d action. actionmappingparams Gestion du mapping des paramètres des actions

49 Object Graph Navigation Language OGNL OGNL est un langage de description permettant d accéder et de modifier les propriétés des objets Java. OGNL fait partie du projet opensymphony ( et et peut être utilisé en complément des taglibs Java EE. OGNL fait référence aux objets en fonction de leur portée et de la collection de données (Context Map) : application : cette collection contient les attributs présents dans le contexte de l application ServletContext. session : cette collection contient les attributs présents dans la session de l utilisateur. request : cette collection contient tous les attributs présents dans la requête courante. parameters : cette collection contient les paramètres de la requête courante. attr : cette collection recherche les attributs dans l ordre suivant : request, session et application. Pour lire des objets dans la pile de propriétés, nous pouvons utiliser la balise <s:property value= nomdelapropriete /> avec le paramètre value adapté. Comme les paramètres de la pile sont référencés par l action, le caractère dièse (#) est optionnel. Par contre, lorsque nous souhaitons accéder à des objets dans le contexte (session, application ou attr) nous devons utiliser la notation #. Par exemple les notations suivantes permettent de lire des paramètres suivant la portée : <s:property value="client.identifiant"/> <s:property value="#request.client[ identifiant ]"/> <s:property value="#session.motdepasse"/> <s:property value="#application. contat"/> La notation pointée (et par tableau) est utilisée pour accéder à des objets du contexte. Nous pouvons accéder par exemple à la propriété identifiant de l objet client sous les formes suivantes : <s:property value="client.identifiant"/> <s:property value="client[ identifiant ]"/> <s:property value="#request.client[ identifiant ]"/> OGNL permet également de déclencher des méthodes de classes afin d afficher un traitement spécifique à l aide du caractère Pour cela, nous devons préciser dans l appel, le nom pleinement qualifié de la classe suivi du nom de la méthode. Par exemple, pour déclencher la méthode statique Affichage() de la classe statique BoiteOutils présente dans le paquetage exemple39. <s:property value="@exemple39.boiteoutis@affichage"/> OGNL permet également de manipuler des tableaux de valeurs (Array) par l intermédiaire du getter associé et des différentes propriétés dédiées. Le tableau de pays déclaré dans la classe d action est utilisé dans la vue par l intermédiaire d OGNL. // liste de pays private String[] listepays; listepays=new String[] "France","Angleterre","Allemagne","Suisse"; Nous pouvons afficher directement le contenu du tableau par l intermédiaire de son nom, sa taille avec l attribut length et accéder à des éléments par indice. Liste des pays : <s:property value="listepays"/><br/> Liste des pays : <s:property value="[0].listepays"/> <br> Taille du tableau des pays : <s:property value="listepays.length"/ ><br/> Accès au premier indice du tableau : <s:property value="listepays[0]"/><br/> - 1 -

50 OGNL permet également de manipuler des listes de valeurs sous la forme de clés/valeurs (Map) par l intermédiaire du getter associé et des différentes propriétés dédiées. La liste des villes déclarées dans la classe d action est utilisée dans la vue par l intermédiaire d OGNL. // liste des villes private Map<String,String> listevilles=new HashMap<String,String>(); listevilles.put("pa", "Paris"); listevilles.put("ma", "Marseille"); listevilles.put("mo", "Monaco"); Nous pouvons afficher directement le contenu de la liste par l intermédiaire de son nom, sa taille avec l attribut size et accéder à des éléments par clé. Liste des villes : <s:property value="listevilles"/><br/> Liste des villes : <s:property value="[0].listevilles"/> <br> Taille de la liste des villes : <s:property value="listevilles.size"/><br/> Accès au premier indice du tableau : <s:property value="listevilles[ MO ]"/><br/> OGNL permet également de manipuler des listes d objets (List) par l intermédiaire du getter associé et des différentes propriétés dédiées. La liste des clients déclarés dans la classe d action est utilisée dans la vue par l intermédiaire d OGNL. // liste de clients private List<Client> listeclients=new ArrayList<Client>(); Client c1=new Client(); c1.setidentifiant("jlafosse"); c1.setmotdepasse("jerome"); Client c2=new Client(); c2.setidentifiant("astapane"); c2.setmotdepasse("amelie"); listeclients.add(c1); listeclients.add(c2); Nous pouvons afficher directement le contenu de la liste par l intermédiaire de son nom, sa taille avec l attribut size et accéder à des éléments par clé. Liste des clients : <s:property value="listeclients"/><br/> Liste des clients : <s:property value="[0].listeclients"/> <br> Taille de la liste des clients : <s:property value="listeclients.size"/><br/> Accès au premier indice du tableau : <s:property value="listeclients[0].getidentifiant()"/><br/> Liste vide? : <s:property value="listeclients.isempty"/><br/> OGNL permet de déclencher des méthodes de la classe d action. La technique est la même que pour l accès à des objets de la classe et de l internationalisation. // méthode de la classe public String getaffichage() return "Méthode de la classe d action : "+new GregorianCalendar().getTime(); Nous pouvons afficher directement le résultat de l exécution de la méthode d action par l intermédiaire de son nom. Déclencher une méthode de classe d action : <s:property value="%getaffichage()"/> Le nouveau projet exemple39 reprend en détail les exemples précédents et présente les différentes manipulations OGNL

51 Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple39" namespace="/" extends="strutsdefault"> <default-action-ref name="ognl" /> </package> </struts> <action name="ognl" class="exemple39.ognlaction"> <result>/jsp/ognl.jsp</result> </action> Code : exemple39.ognlaction.java package exemple39; import java.util.arraylist; import java.util.gregoriancalendar; import java.util.hashmap; import java.util.list; import java.util.map; import org.apache.struts2.interceptor.sessionaware; import com.opensymphony.xwork2.actionsupport; import public class OgnlAction extends ActionSupport implements SessionAware // objet client private Client client; // objet session private Map<String,Object> sessionmap; // tableau de pays private String[] listepays; listepays=new String[] "France","Angleterre","Allemagne","Suisse"; // liste des villes private Map<String,String> listevilles=new HashMap<String,String>(); listevilles.put("pa", "Paris"); listevilles.put("ma", "Marseille"); listevilles.put("mo", "Monaco"); // liste de clients private List<Client> listeclients=new ArrayList<Client>(); Client c1=new Client(); c1.setidentifiant("jlafosse"); c1.setmotdepasse("jerome"); Client c2=new Client(); c2.setidentifiant("astapane"); c2.setmotdepasse("amelie"); - 3 -

52 listeclients.add(c1); listeclients.add(c2); public Client getclient() return client; public void setclient(client client) this.client = client; public void setsession(map<string,object> map) this.sessionmap=map; public String[] getlistepays() return listepays; public Map<String, String> getlistevilles() return listevilles; public List<Client> getlisteclients() return listeclients; // méthode de la classe public String getaffichage() return "Méthode de la classe d action : "+new GregorianCalendar().getTime(); // ajouter les informations du client dans la session public String execute() // création d un objet client client=new Client(); client.setidentifiant("jlafosse"); client.setmotdepasse("jerome"); // ajouter le mot de passe dans la session sessionmap.put("motdepasse", "lafosse"); return SUCCESS; Code : /jsp/ognl.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ognl</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <h4>informations OGNL</h4> Lecture du client dans la requête : <s:property value="client"/><br/> Lecture du client dans la requête : <s:property value="client.identifiant"/><br/> Lecture du client dans la requête : <s:property value="client[ identifiant ]"/><br/> Lecture du client dans la requête : <s:property value="#request.client[ identifiant ]"/><br/> <hr/> - 4 -

53 Lecture des informations dans la session : <s:property value="#session.motdepasse"/><br/> Lecture des informations dans la session : <s:property value="#session[ motdepasse ]"/><br/> <hr/> Lecture des informations dans le contexte de l application (web.xml) : <s:property value="#application. contat"/><br/> <hr/> Manipulation de tableaux (Arrays)<br/> Liste des pays : <s:property value="listepays"/><br/> Liste des pays : <s:property value="[0].listepays"/> <br> Taille du tableau des pays : <s:property value="listepays.length"/><br/> Accès au premier indice du tableau : <s:property value="listepays[0]"/><br/> <hr/> Manipulation de listes (Map)<br/> Liste des villes : <s:property value="listevilles"/><br/> Liste des villes : <s:property value="[0].listevilles"/> <br> Taille de la liste des villes : <s:property value="listevilles.size"/><br/> Accès au premier indice du tableau : <s:property value="listevilles[ MO ]"/><br/> <hr/> Manipulation de listes (List)<br/> Liste des clients : <s:property value="listeclients"/><br/> Liste des clients : <s:property value="[0].listeclients"/> <br> Taille de la liste des clients : <s:property value="listeclients.size"/><br/> Accès au premier indice du tableau : <s:property value="listeclients[0].getidentifiant()"/><br/> Liste vide? : <s:property value="listeclients.isempty"/><br/> <hr/> Déclencher une méthode de classe d action : <s:property value="%getaffichage()"/> <hr/> Liste créée avec OGNL<br/> <s:select label="label de la liste" name="nom" list=" valeur 1, valeur 2, valeur 3 "/> </div> </body> </html> - 5 -

54 Arborescence du projet exemple39 Manipulation des informations à l aide d OGNL - 6 -

55 En résumé Ce dernier chapitre a présenté en détail la liste des intercepteurs utilisés par le framework Struts. De même, la section Object Graph Navigation Language OGNL explique de façon exhaustive le langage de manipulation OGNL permettant d accéder et modifier les objets Java

56 Présentation Comme expliqué précédemment le modèle de conception Modèle Vue Contrôleur (MVC) est recommandé pour les développements d applications web en Java. Avant de commencer la conception, il est important de bien comprendre le principe de ce modèle de développement. L architecture MVC proposée par Sun est la solution de développement web côté serveur qui permet de séparer la partie logique/métier de la partie présentation dans une application Internet. C est un point essentiel du développement de projets car cela permet à toute l équipe de travailler séparément (chaque personne gère ses fichiers, ses logiciels de développement et ses composants). Cette architecture trouve son origine dans le langage SmallTalk au début des années 1980, ce n est donc pas un nouveau modèle (design pattern) uniquement lié à Java EE. L objectif principal étant de diviser l application en trois parties distinctes : le modèle, la vue et le contrôleur. Dans l architecture MVC nous retrouvons : Le modèle représenté par les EJB et/ou JavaBeans et/ou systèmes de persistance (Hibernate, objets sérialisés en XML, stockage de données par le biais de JDBC...). La vue représentée par les JSP ou classes SWING. Le contrôleur représenté par les Servlets ou classes Java. Architecture MVC I Principe de fonctionnement de l architecture MVC Le client envoie une requête HTTP au serveur. C est en général une Servlet (ou un programme exécutable côté serveur) qui traite la demande. La Servlet récupère les informations transmises par le client et délègue le traitement à un composant métier adapté. Les composants du modèle manipulent ou pas des données du système d information (lecture, écriture, mise à jour, suppression). Une fois les traitements terminés, les composants rendent la main à la Servlet en lui retournant un résultat. La Servlet stocke alors le résultat dans un contexte adapté (session, requête, réponse...). La Servlet appelle la page JSP adéquate qui peut accéder au résultat. La JSP s exécute, utilise les données transmises par la Servlet et génère la réponse au client. Dans les projets simples, les requêtes HTTP sont gérées par des composants web qui reçoivent les requêtes, créent les réponses et les retournent aux clients. Nous avons donc un seul composant responsable de la logique d affichage, de la logique métier et de la logique de persistance. Dans l architecture précédente, l affichage et la manipulation des données sont mélangés dans un seul composant Servlet. Cela peut largement convenir pour un service spécifique non évolutif et simple mais cela devient un problème quand le système se développe. Cette architecture conduit à placer du code Java et du code HTML dans les Servlets ou JSP. Il existe plusieurs solutions à ce problème. La plus simple correspond à l apparition des pages JSP et consiste à créer des fichiers d en tête, de pied de page, de traitement... et d inclure le tout dans une page générale. L architecture MVC sépare la logique métier de l affichage. Dans ce modèle, un composant est chargé de recevoir les requêtes (Servlets), un autre traite les données (Classes) et un troisième gère l affichage (JSP). Si l interfaçage entre ces trois composants est clairement défini, il devient plus simple de modifier un composant sans toucher aux deux autres. Dans une application web évoluée, la logique MVC est la suivante : Le client émet des requêtes au serveur. Chaque action précise correspond à une Servlet qui redirige les requêtes vers une page JSP adéquate ou réalise un traitement ou accède à des données et dans ce cas, déclenche un autre - 1 -

57 programme qui sera chargé de répondre à la demande de l utilisateur courant. Le schéma suivant présente une structure de type MCV avec l utilisation de Servlets et pages JSP. Architecture MVC, Servlets et JSP Les Servlets jouant le rôle de contrôleur dans une application MVC doivent disposer d un moyen pour transmettre les requêtes aux composants chargés de l affichage. Ce moyen est fourni par l objet RequestDispatcher. Ce composant permet de faire suivre une requête d un composant vers un autre. Nous obtenons un objet RequestDispatcher avec la méthode getservletcontext(). À partir de cet objet, il est possible d obtenir un RequestDispatcher à l aide des méthodes suivantes :getnameddispatcher(nom) ou getrequestdispatcher (chemin). La méthode getrequestdispatcher(...) fonctionne avec un chemin qui commence par la barre oblique et qui est relatif au contexte de l application. La méthode getnameddispatcher(...) correspond à un sous élément <servletname> d un élément <servlet-mapping> du descripteur de déploiement web.xml. Les composants sont bien sûr plus nombreux mais également plus simples. Leurs spécificités font qu ils pourront être développés par des spécialistes : les Servlets et EJB par des développeurs Java, les JSP par des développeurs et Webdesigners, les accès aux données par des spécialistes SQL... Ce découpage permet également une maintenance plus aisée du système. Nous pourrons ainsi opérer facilement au changement de la charte graphique en utilisant les vues sans toucher au modèle et contrôleur. Dans le modèle de conception MVC, nous avons une Servlet ou un filtre qui représente le contrôleur du modèle. Struts 1 utilise une Servlet alors que Struts 2 emploie un filtre. Pour le modèle nous utilisons des POJO (Plain Old Java Object) qui sont de simples objets en opposition aux EJB. L acronyme POJO est utilisé pour faire référence à la simplicité d utilisation d un objet Java en comparaison avec la lourdeur d utilisation d un composant EJB (Enterprise Java Bean). Les JavaBeans (à ne pas confondre avec les EJB) sont des composants logiciels simples réutilisables et manipulables. Pour être une classe JavaBean, celle ci devra respecter certaines conventions de nommage afin de respecter l utilisation, la réutilisation, le remplacement et la connexion JavaBean. La classe doit être sérialisable (pour les sauvegardes et lectures). La classe doit avoir un constructeur par défaut (sans argument). Les propriétés des méthodes doivent être accessibles via des méthodes (accesseurs). La seule différence réelle entre un POJO et un JavaBean étant la possibilité de gérer des événements pour les JavaBeans. Avec le modèle MVC, chaque requête HTTP doit être envoyée au contrôleur. La syntaxe de l URI (Uniform Resource Identifier) indique au contrôleur quelle commande doit être déclenchée. Avec Struts 2, une classe d action peut exécuter plusieurs opérations sur le même principe que les MappingDispatchAction de Struts

58 Premier projet MVC Nous allons commencer par un exemple simple de formulaire d ajout d un nouveau client (identifiant et mot de passe) et l affichage de ses informations saisies. L application est nommée exemple01. La personne va saisir un identifiant et un mot de passe et ces informations seront ensuite affichées dans une autre page à la suite de la création, sans persistance d un objet client. L application est composée des éléments suivants : Une classe nommée Client qui est un JavaBean. Une Servlet contrôleur nommée ServletControleur. Deux pages JSP pour les affichages respectifs du formulaire de saisie et des données. Une feuille de styles nommée styles.css pour la mise en forme des affichages. Arborescence du projet exemple01 Le fichier source Client.java est une simple classe POJO. Code : exemple01.client.java package exemple01; import public class Client implements Serializable private String identifiant; private String motdepasse; public Client() public String getidentifiant() return identifiant; public void setidentifiant(string identifiant) this.identifiant = identifiant; public String getmotdepasse() return motdepasse; - 1 -

59 public void setmotdepasse(string motdepasse) this.motdepasse = motdepasse; La Servlet ServletControleur, hérite de la classe javax.servlet.http.httpservlet et permet de traiter les requêtes HTTP de type Post et Get. Le code de cette Servlet est assez simple, il analyse l URI, exécute l action et la redirection adaptée en conséquence. Par exemple, si l URI est ValiderAjouter_client.action, un objet client est créé à partir des données saisies dans le formulaire et l internaute est redirigé vers la page JSP AfficherClient.jsp. Code : exemple01.servletcontroleur.java package exemple01; import java.io.ioexception; import javax.servlet.servletexception; import javax.servlet.http.httpservlet; import javax.servlet.http.httpservletrequest; import public class ServletControleur extends HttpServlet public void dopost(httpservletrequest request, HttpServletResponse response)throws IOException, ServletException // uri est de la forme suivante /exemple01/ajouter_client.action String uri=request.getrequesturi(); // on utilise le code pour découper cette URI int lastindex=uri.lastindexof("/"); String action=uri.substring(lastindex+1); System.out.println("ACTION : "+action); // pour les redirections String urlretour=null; // exécuter l action adaptée if (action.equals("ajouter_client.action")) urlretour="/jsp/ajoutermodifierclient.jsp"; else if (action.equals("validerajouter_client.action")) // instancier l objet Client client=new Client(); // mise à jour de l objet client.setidentifiant(request.getparameter("identifiant")); client.setmotdepasse(request.getparameter("motdepasse")); // retourner l objet dans la vue request.setattribute("client", client); //page d affichage du client urlretour="/jsp/afficherclient.jsp"; // redirection vers la vue adaptée if (urlretour!=null) request.getrequestdispatcher(urlretour).forward(request, response); public void doget(httpservletrequest request, HttpServletResponse response)throws IOException, ServletException dopost(request, response); - 2 -

60 Enfin, les deux vues sont très simples. Elles permettent d afficher le formulaire de saisie d un nouveau client (AjouterClient.jsp) ainsi que les données de ce même client (AfficherClient.jsp). Code : /jsp/ajouterclient.jsp <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <form method="post" action="validerajouter_client.action"> <table> <tr> <td>identifiant:</td> <td><input type="text" name="identifiant"/></td> </tr> <tr> <td>mot de passe:</td> <td><input type="text" name="motdepasse"/></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="ajouter le client"/></td> </tr> </table> </form> </div> </body> </html> Code : /jsp/afficherclient.jsp <html> <head> <title>afficher le client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <p> <h4>informations sur le client:</h4> Identifiant: $client.identifiant<br/> Mot de passe: $client.motdepasse<br/> </p> </div> </body> </html> Le fichier de configuration de l application, également appelé descripteur de déploiement web.xml, est très simple. Il possède une seule définition de Servlet permettant de gérer toutes les URIs qui se terminent par.action. Nous pourrons ainsi gérer sans problème nos actions Ajouter_client.action et Afficher_client.action. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="iso "?> <web-app xmlns=" xmlns:xsi=" xsi:schemalocation=" version="2.5"> <servlet> <servlet-name>servletcontroleur</servlet-name> <servlet-class>exemple01.servletcontroleur</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletcontroleur</servlet-name> <url-pattern>*.action</url-pattern> - 3 -

61 </servlet-mapping> </web-app> Le modèle *.action permet de ne pas écouter toutes les URI (*) ce qui serait inutile pour les ressources dites statiques comme les fichiers JavaScript (.js), les images ou feuilles de style CSS (.css). Ces fichiers seront alors ignorés par notre contrôleur général. Nous pouvons tester cette application, avec l URL suivante : Formulaire d ajout client exemple01 Lorsque l utilisateur valide la création d un client, l URL suivante est alors déclenchée : Affichage des informations client exemple01-4 -

62 Projet MVC avec filtre L application précédente repose sur une Servlet de gestion permettant d effectuer les actions adaptées en fonction de la demande de l internaute. Les filtres permettent de donner à une application une structure modulaire. Ils permettent d encapsuler différentes tâches qui peuvent être indispensables pour traiter des requêtes. La principale fonction d une Servlet est de recevoir les requêtes et de répondre aux clients concernés. Par contre, il est très souvent nécessaire de réaliser une fonction identique pour chaque Servlet en rapport avec les requêtes et réponses HTTP. Nous souhaitons router ou transporter un paramètre dans toutes les requêtes sans être obligé d écrire le code pour chaque Servlet... L interface Filter apparue avec l API Servlet 2.3 permet de résoudre ce type de problème. Les filtres permettent ainsi de traiter : les requêtes venant des clients avant qu elles ne soient traitées par les Servlets ; les réponses venant des Servlets avant qu elles ne soient retournées aux clients. Nous pouvons par exemple : décrypter des requêtes envoyées aux Servlets, traiter les données avec les Servlets et crypter les réponses pour les clients ; gérer l authentification des clients ; convertir des formats d images, appliquer des transformations XSLT sur des données XML ou autre. Pour utiliser un filtre il est nécessaire de réaliser deux opérations. La première consiste à écrire une classe qui implémente l interface Filter. La seconde consiste à modifier le descripteur de déploiement (fichier web.xml) de l application pour indiquer au conteneur d utiliser le filtre. Lorsqu un filtre est créé, le conteneur appelle sa méthode init(...). Dans cette méthode, nous pouvons accéder aux paramètres d initialisation avec l interface FilterConfig. Lors du traitement de la requête, le conteneur appelle la méthode dofilter(...). Enfin, avant de détruire le filtre, le conteneur appelle sa méthode destroy(...). Lorsque le filtre appelle la méthode chain.dofilter(), le filtre suivant dans la chaîne est exécuté. Le code placé avant chain.dofilter() est exécuté avant le traitement de la Servlet. Toute modification que le filtre doit apporter avant l exécution de la requête doit être effectuée avant cet appel. Le code placé après cet appel est exécuté à la suite du traitement de la Servlet. Il est tout à fait possible de chaîner les filtres et d utiliser un filtre par traitement spécifique. Le schéma suivant présente le fonctionnement d un filtre avant et après traitement par la Servlet invoquée. Structure d un filtre Java EE Le descripteur de déploiement web.xml permet d indiquer le ou les filtres à déclencher pour chaque Servlet ou URL. Le premier élément <filter> permet de déclarer la classe associée au filtre. Cet élément doit être placé en début de - 1 -

63 fichier de configuration après la déclaration des variables globales au contexte (<context-param>). Dans notre cas, nous définissons un filtre associé à la classe FiltreJournalisation permettant de gérer les accès aux pages du site.... <!-- definition du filtre --> <filter> <filter-name>filtrejournalisation</filter-name> <filter-class>boiteoutils.filtrejournalisation</filter-class> </filter>... Le second élément nécessaire est <filter-mapping>. Il permet comme pour les Servlets, de gérer le mapping (relations) entre un nom et une Servlet ou une URL. Par exemple, ici le filtre est appliqué uniquement à la Servlet servletauthentification.... <filter-mapping> <filter-name>filtrejournalisation</filter-name> <servlet-name>servletauthentification</servlet-name> </filter-mapping>... Avec l utilisation d un filtre, nous allons pouvoir offrir à chaque ressource (dynamiques et statiques) de notre application un unique contrôleur (une Servlet). Avec l exemple précédent du descripteur de déploiement web.xml, seules les URIs suffixées par.action sont traitées par notre Servlet. Nous ne gérons pas les ressources statiques comme les images, fichiers JavaScript ou feuilles de styles. <servlet> <servlet-name>servletcontroleur</servlet-name> <servlet-class>exemple01.servletcontroleur</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletcontroleur</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> Le fonctionnement d un filtre est différent, chaque ressource est traitée dans la fonction filterchain.dofilter() que ce soient les requêtes ou les éléments statiques. Le principal avantage étant de pouvoir gérer également les ressources statiques et d interdire les accès directs. La nouvelle application est nommée exemple01 2. Ce projet est basé sur le précédent mais le fichier de configuration de l application web.xml utilise désormais un filtre en lieu et place d une définition de Servlet. La classe Client et les pages JSP sont identiques, cependant nous allons développer un filtre nommé FiltreControleur à la place du fichier ServletControleur. Code : exemple01.filtrecontroleur.java package exemple01; import java.io.ioexception; import javax.servlet.filter; import javax.servlet.filterchain; import javax.servlet.filterconfig; import javax.servlet.servletexception; import javax.servlet.servletrequest; import javax.servlet.servletresponse; import javax.servlet.http.httpservletrequest; import public class FiltreControleur implements Filter private FilterConfig filterconfig; public void init(filterconfig filterconfig) throws ServletException this.filterconfig=filterconfig; - 2 -

64 public void dofilter(servletrequest req, ServletResponse resp, FilterChain filterchain)throws IOException, ServletException // transtypage HttpServletRequest request=(httpservletrequest)req; HttpServletResponse response=(httpservletresponse)resp; // uri est de la forme suivante /exemple01-2/ Ajouter_client.action String uri=request.getrequesturi(); // on utilise le code pour découper cette URI int lastindex=uri.lastindexof("/"); String action=uri.substring(lastindex + 1); System.out.println("ACTION : "+action+" avec un filtre"); // pour les redirections String urlretour=null; // exécuter l action adaptée if (action.equals("ajouter_client.action")) urlretour="/jsp/ajouterclient.jsp"; else if (action.equals("validerajouter_client.action")) // instancier l objet Client client=new Client(); // mise à jour de l objet client.setidentifiant(request.getparameter("identifiant")); client.setmotdepasse(request.getparameter("motdepasse")); // retourner l objet dans la vue request.setattribute("client", client); //page d affichage du client urlretour="/jsp/afficherclient.jsp"; // redirection vers la vue adaptée if (urlretour!=null) request.getrequestdispatcher(urlretour).forward(request, response); // pour les ressources statiques else filterchain.dofilter(request, response); public void destroy() this.filterconfig=null; Le code du contrôleur est identique du point de vue algorithmique. L internaute peut afficher le formulaire de création et après validation, visualiser ses données à travers un objet client. Le code du descripteur de déploiement est modifié afin de déclarer un filtre à l écoute des requêtes. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="iso "?> <web-app xmlns=" xmlns:xsi=" xsi:schemalocation=" version="2.5"> - 3 -

65 <filter> <filter-name>filtrecontroleur</filter-name> <filter-class>exemple01.filtrecontroleur</filter-class> </filter> <filter-mapping> <filter-name>filtrecontroleur</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> Nous pouvons utiliser cette application avec l URL suivante pour ajouter un client : 2/Ajouter_client.action Console et traces du filtre - 4 -

66 En résumé Ce chapitre a présenté le mécanisme de développement web en Java avec le modèle de conception MVC Modèle Vue Contrôleur. Nous avons ensuite observé deux techniques de programmation. La première utilise le principe de mapping des URIs pour intercepter les requêtes HTTP à l aide d une Servlet contrôleur. La seconde introduit le mécanisme de filtre cher à Struts 2. Dans le chapitre suivant, nous allons écrire notre première application avec le framework Struts

67 Présentation Ce chapitre présente le framework Struts 2 au travers d un exemple concret. Afin d accélérer et faciliter le développement d applications web. À partir de cette étape de l ouvrage, le terme Struts sera utilisé pour faire référence à Struts version 2. Les applications Struts possèdent un fichier de configuration nommé struts.xml. Ce fichier de configuration est le plus important et remplace la définition des Servlets dans le descripteur de déploiement web.xml. En effet, il permet de gérer la configuration des actions à réaliser. Struts possède également un fichier de propriétés présent par défaut dans l archive de l application struts2 coreversion.jar nommé default.properties. Ce fichier utilisé par défaut, contient les textes de validation (messages d erreurs et de succès) et les paramètres de configuration de Struts. Ce fichier par défaut suffit pour commencer avec une application simple (en anglais). Comme nous l avons précisé dans le chapitre précédent, Struts utilise un filtre pour réaliser le routage vers un seul contrôleur de gestion correspondant au modèle MVC II. Le contrôleur Struts est alors capable de : Déterminer l URI pour l action à déclencher. Utiliser une classe d action. Déclencher la méthode d action de la classe si celle ci est associée. Si des saisies ont été réalisées, créer un objet et le mettre à jour ou positionner les valeurs des paramètres. Retourner vers la vue (page JSP) pour afficher la réponse. Grâce à l utilisation d un filtre et d un contrôleur global, nous n avons pas à écrire un contrôleur complexe pour chaque service (ex : gestion de clients, d articles ) afin de gérer le routage de l application. Le plus important pour le développeur étant de gérer les actions associées à une demande spécifique

68 Fonctionnement général de Struts 2 Struts utilise un filtre comme dans le cas de l application exemple01 2. Ce filtre doit être déclaré dans le descripteur de déploiement de notre application web.xml. Ce filtre est défini dans la classe org.apache.struts2.dispatcher.ng.filter.strutsprepareandexecutefilter. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="utf-8"?> <web-app id="webapp_9" version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <display-name>struts Blank</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filterdispatcher</ filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> Avec Struts, la méthode d action de la classe est exécutée après que toutes les propriétés soient traitées et affectées. De son côté, une méthode d action retourne une chaîne de caractères de type String. Cette chaîne indique à Struts où le contrôleur doit se rediriger. Par exemple, la chaîne success indique à Struts de retourner vers la page en cas de traitement correct et la chaîne error précise la page à afficher en cas d erreur. Dans la plupart des cas, Struts redirige l utilisateur vers une vue JSP à l aide de l interface RequestDispatcher de Java. En général, les vues en retour sont au format JSP mais peuvent également être des modèles Velocity ou FreeMarker. De même, Struts peut très bien retourner un flux multimédia de type image par exemple. Pour tester et analyser les URIs, Struts utilise le fichier de configuration nommé struts.xml. Ce fichier de configuration doit être placé dans le répertoire/web INF/src (ou /WEB INF/classes après compilation) pour un fonctionnement par défaut. Toutes les actions sont alors déclarées dans ce fichier de routage et un nom d URI correspond à une déclaration d action. Ce fichier de configuration struts.xml est lu au démarrage de l application. Nous pouvons par simplicité et afin d éviter de recharger le gestionnaire, déclarer l application en mode développement. Avec ce mode de conception, le fichier sera rechargé à chaque changement de l application. Avec cette balise, il n est pas nécessaire de recharger le conteneur. Code : struts.xml... <constant name="struts.devmode" value="true" />... Chaque déclaration d action est associée à une classe pleinement qualifiée (nompaquetage.nomclasse). Dans le cas contraire, nous pouvons définir une action Struts par défaut. Une classe d action doit également posséder au moins un résultat de type chaîne de caractères mais peut en avoir plusieurs (succès, erreur, consultation, liste ). Lors de l accès à une ressource ou le déclenchement d une action, Struts utilise ce processus d appel : - 1 -

69 Processus d exécution Struts 1) Le client envoie des requêtes à partir d URLs adaptées. Il peut éventuellement envoyer des paramètres dans l URL ou par l intermédiaire d un formulaire de saisie. 2) Struts consulte son fichier de configuration struts.xml afin de retrouver la configuration de l action. 3) Chaque intercepteur associé à l action est déclenché. L un de ces intercepteurs est chargé d assigner automatiquement les valeurs reçues dans la requête aux propriétés de la classe d action, en fonction des noms (ex : identifiant, motdepasse). 4) Struts exécute la méthode d action associée à la classe. 5) Le résultat adapté est retourné à l utilisateur demandeur. Les intercepteurs sont encore lancés après que la méthode d action soit exécutée, ce qui permet à ces mêmes intercepteurs d exécuter des opérations sur les paramètres après traitement de la méthode d action

70 Les intercepteurs Struts 2 Un intercepteur (interceptor) Struts est un filtre qui peut effectuer différents traitements sur une action. Le code présent dans un intercepteur est modulable et peut être ajouté ou supprimé directement dans le fichier de configuration struts.xml. Du code spécifique peut ainsi être ajouté à une application sans recompiler le framework principal. À cette étape du livre, il est juste nécessaire de comprendre qu un intercepteur est un filtre jouant un rôle spécifique et permet par exemple de gérer les cookies, paramètres HTTP, le débogage, le chargement de fichiers (upload) ou encore les alias d actions

71 Le fichier de configuration struts.xml Une application Struts possède un fichier de configuration struts.xml au format XML et le fichier de propriétés par défaut default.properties mais elle peut posséder d autres fichiers de configuration. Il est possible de ne pas avoir de fichier de configuration dans une application Struts. Ce type de programmation est appelé configuration zéro (zero configuration). Cette technique utilise les annotations Java dans les classes afin de ne pas déclarer les actions dans un fichier de configuration. Dans le fichier struts.xml nous allons définir la configuration générale de l application : Les paramètres de configuration de Struts. Les actions. Les intercepteurs. Les résultats des actions. Le fichier de configuration struts.xml ci dessous est livré en standard avec l application struts blank.war. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="false" /> <include file="example.xml"/> <package name="default" namespace="/" extends="struts-default"> <default-action-ref name="index" /> <action name="index"> <result type="redirectaction"> <param name="actionname">helloworld</param> <param name="namespace">/example</param> </result> </action> </package> </struts> 1. La balise <package/> Les actions Struts sont regroupées par paquetage selon un principe semblable à celui des paquetages Java. Dans l exemple précédent, le paquetage est nommé default. Une balise <package/> doit avoir un attribut name afin de référencer le paquetage. L attribut optionnel namespace possède la valeur par défaut /. Cet espace de nom est toujours ajouté aux URIs qui déclenchent les actions du paquetage. Les attributs de la balise <package/> sont les suivants : name : attribut obligatoire précisant le nom du paquetage. namespace : attribut précisant l espace de nommage pour toutes les actions du paquetage. extends : attribut obligatoire précisant le paquetage parent à hériter

72 abstract : attribut peu utilisé précisant si le paquetage peut être hérité ou non. Une URI est invoquée selon le modèle suivant : /contextapplication/nomaction.action (ou nomaction.do). Maintenant, pour invoquer une action dans un espace de nom, nous devons utiliser la syntaxe suivante : /contextapplication/espacedenom/nomaction.action (ou nomaction.do) Par exemple, pour déclencher la partie administration du projet exemple01 définit comme suit, nous utilisons l URI suivante : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts>... <package name="exemple01" namespace="/admin" extends="strutsdefault">... </package>... </struts> La balise <package/> doit dans la plupart des cas étendre le paquetage struts default définit dans le fichier strutsdefault.xml. Dans ce cas, toutes les actions peuvent utiliser les intercepteurs déclarés dans le fichier struts default.xml. Les développeurs peuvent créer leurs propres paquetages en héritant de ce paquetage par défaut qui met à disposition la plupart des intercepteurs utiles pour la programmation d applications Internet. 2. La balise <include/> Comme nous l avons précisé précédemment, le fichier de configuration Struts se nomme struts.xml. Dans une application complexe, nous pouvons avoir plusieurs paquetages et une grande quantité de lignes dans ce fichier de configuration. Afin d améliorer la gestion et la maintenance de ce fichier, il est possible de découper ce fichier en plusieurs sous fichiers. Chaque sous fichier est alors inclus dans ce fichier principal. La balise <include/> permet de découper le fichier principal en sous fichiers. Dans l idéal, chaque sous fichier définit son propre paquetage. Nous pouvons par exemple découper le fichier de configuration de l application pour les parties frontoffice et backoffice du site. Chaque fichier inclus doit utiliser la même grammaire (DOCTYPE) et la balise racine du document <struts/>. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="false" /> <include file="frontoffice.xml"/> <include file="backoffice.xml"/> </struts> Code : frontoffice.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="default" namespace="/" extends="strutsdefault"> <default-action-ref name="index" /> <action name="index"> <result type="redirectaction"> - 2 -

73 <param name="actionname">helloworld</param> <param name="namespace">/example</param> </result> </action> </package> </struts> 3. La balise <action/> La balise <action/> est utilisée avec une balise <package/> et représente l action fournie par le paquetage. Une action doit avoir un nom. Le nom est le choix du développeur mais il doit être le plus explicite possible afin d améliorer la maintenance. Une action est en général associée à une classe mais cela n est pas obligatoire pour les redirections. Une action qui ne précise pas de classe utilise une instance de la classe par défaut, ActionSupport. La syntaxe est alors la suivante : <action name="uneaction"/> Par contre, si une classe est définie, le nom de la classe doit être pleinement qualifié (nompaquetage.nomclasse). <action name="uneaction" class="nompaquetage.nomclasse"/> Nous pouvons également spécifier le nom de la méthode de la classe qui doit être exécutée lors du déclenchement de l action. La syntaxe est alors la suivante : <action name="uneaction" class="nompaquetage.nomclasse" method="nommethode"/> Si l attribut class est présent dans la définition mais que l attribut method n est pas précisé, par défaut c est la méthode execute() de la classe qui sera utilisée lors du déclenchement de l action. Ainsi, les déclarations suivantes sont identiques : <action name="ajouter_client" class="exemple02.actions.client" method="execute"/> <action name="ajouter_client" class="exemple02.actions.client"/> 4. La balise <result/> La balise <result/> est utilisée à l intérieur d une balise <action/> comme sous élément. Cette balise permet d indiquer à Struts où retourner le résultat de l action. Une action peut très bien retourner plusieurs résultats (tous différents), elle peut donc avoir plusieurs balises <result/> chacune correspondant à un résultat possible. Par exemple, une action peut retourner sur une page d erreur en cas de problème de syntaxe lors des saisies (input) ou sur une page de succès pour afficher le résultat (success). Voici l exemple de configuration pour notre projet de création d un compte client à partir de l identifiant et du mot de passe : <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficher.jsp</result> </action> Le premier résultat est exécuté si la méthode ajouter() retourne la chaîne de caractères success. Dans ce cas, la page Afficher.jsp est affichée dans le navigateur. Le deuxième résultat est exécuté si la méthode ajouter() retourne la chaîne de caractères input lors d une erreur de saisie. Dans ce cas, la page de saisie est affichée à nouveau sans perte des données. L attribut type de la balise <result/>, précise le type de résultat à retourner. Ce type doit être spécifié dans le paquetage ou un paquetage hérité. Dans le cas d une redirection par exemple, le type dispatcher est défini dans le paquetage struts default. Si nous n utilisons par l attribut type dans la balise <result/>, la valeur dispatcher (redirection) est utilisée par défaut. Si nous n utilisons pas l attribut name dans la balise <result/>, la valeur success est alors utilisée par défaut

74 Ainsi, les déclarations suivantes sont identiques : <result name="success" type="dispatcher">/jsp/afficherclient.jsp</ result> <result>/jsp/afficherclient.jsp</result> Si une méthode d une classe d action retourne un résultat qui n est pas présent dans une balise <result/> de l action, Struts cherche alors un résultat correspondant dans le groupe <global-results/>. Cette balise permet de réaliser des groupements de balises pour éviter les définitions répétitives. Nous retrouvons par exemple dans cette balise, les redirections vers la page d accueil du site, la page d erreur, d authentification ou d administration. 5. La balise <param/> La balise <param/> peut être utilisée avec les éléments <action/>, <result-type/> et <interceptors/> pour assigner une valeur à l objet concerné. La balise <param/> possède un attribut name pour nommer la valeur. Dans le cas de l utilisation avec une action, la balise <param/> permet d assigner une valeur à une propriété. Par exemple, l identifiant par défaut pour le client est précisé avec la balise <param/>. <action name="ajouter_client" class"..."> <param name="identifiant">jlafosse</param> </action 6. La balise <constant/> Le fichier de configuration struts.xml peut être associé de façon optionnelle, avec le fichier de propriétés struts.properties. Nous pouvons créer des paires de clés/valeurs pour modifier les valeurs par défaut définies dans le fichier default.properties inclus en standard dans le paquetage struts2 core version.jar. Pour modifier ou surcharger une valeur du fichier default.properties sans utiliser le fichier struts.properties, nous pouvons utiliser la balise <constant/> dans le fichier struts.xml. La balise <constant> possède les attributs name et value. Par exemple, pour passer Struts en mode développement pour la gestion des traces et débogages, nous pouvons utiliser la configuration suivante : Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.devmode" value="false" />... </struts> 7. La balise <global results/> La balise <global-results/> est utilisée pour définir des résultats généraux. Si une action ne trouve pas de définition de résultat dans sa propre définition, Struts cherche alors un résultat dans la définition de cette balise. La déclaration des résultats dans cette balise est identique à la déclaration présente dans les actions. Si Struts ne trouve aucune correspondance de résultat, en portée locale ou dans la balise <global-results/>, une exception est levée. <global-results> <result name="accueil">/vues/utilisateurs/index.jsp</result> <result name="admin" type="redirectaction">admin/admin.action</result> - 4 -

75 </global-results> 8. La balise <interceptors/> La balise <interceptors/> est utilisée pour définir des intercepteurs (sorte de filtre). Comme expliqué précédemment, une action peut contenir une liste d intercepteurs qui vont opérer sur les actions. Avant de pouvoir utiliser un intercepteur, nous devons déclarer celui ci dans la balise <interceptors/>. Par exemple, le code suivant enregistre deux intercepteurs : validation (pour les validations utilisateur) et alias (pour la gestion des paramètres). <package name="nomdupaquetage" extends="struts-default"> <interceptors> <interceptor name="validation" class="..."/> <interceptor name="alias" class="..."/> </interceptors> </package> Maintenant que les intercepteurs sont enregistrés, nous pouvons les utiliser avec la balise <interceptor-ref/> qui est un sous élément de la balise <action/>. Voici un exemple d utilisation des intercepteurs précédents : <package name="nomdupaquetage" extends="struts-default"> <interceptors> <interceptor name="validation" class="..."/> <interceptor name="alias" class="..."/> </interceptors> <action name="nomaction" class="nompaquetage.nomclasse"> <interceptor-ref name="validation"/> <interceptor-ref name="alias"/> <result>/jsp/afficherclient.jsp</result> </action> </package> L ordre de déclaration des intercepteurs à une grande importance. En effet, il détermine l ordre de déclenchement des intercepteurs pour chaque action. Dans l exemple précédent, l intercepteur validation sera déclenché en premier suivi de l intercepteur alias. Afin d éviter les répétitions de déclarations d intercepteurs, Struts propose des groupes d intercepteurs. Le développeur n aura plus besoin de réaliser de multiples références pour chaque balise <action/> utilisatrice de ces intercepteurs. Voici un exemple de définition d un groupe d intercepteurs : <interceptor-stack name="intercepteurglobal"> <interceptor-ref name="validation"/> <interceptor-ref name="alias"/> </interceptor-stack> Pour faire référence à un groupe d intercepteurs, il suffit d ajouter la balise <interceptor-ref/> et l attribut name au sein d une action. <action name="nomaction" class="nompaquetage.nomclasse"> <interceptor-ref name="intercepteurglobal"/> <result>/jsp/afficherclient.jsp</result> </action> Le paquetage struts default définit plusieurs groupes d intercepteurs. La balise <default-interceptor-ref/> spécifie le groupe d intercepteurs par défaut. <default-interceptor-ref name="defaultstack"/> Si une action a besoin d utiliser d autres intercepteurs que ceux définis par défaut, nous pouvons redéfinir le groupe d intercepteurs par défaut. L intercepteur params est le plus important, car il permet de gérer la copie et l accès automatique aux données des paramètres de la requête, dans nos actions et pour les JavaBeans utilisés. Voici cidessous la liste des intercepteurs Struts utilisés par défaut : <interceptor-stack name="defaultstack"> <interceptor-ref name="exception"/> - 5 -

76 <interceptor-ref name="alias"/> <interceptor-ref name="servletconfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="profiling"/> <interceptor-ref name="scopedmodeldriven"/> <interceptor-ref name="modeldriven"/> <interceptor-ref name="fileupload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="staticparams"/> <interceptor-ref name="params"> <param name="excludeparams">dojo\..*</param> </interceptor-ref> <interceptor-ref name="conversionerror"/> <interceptor-ref name="validation"> <param name="excludemethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludemethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> - 6 -

77 Architecture Struts 2 Le schéma architectural ci dessous présente le fonctionnement du framework basé sur l utilisation d intercepteurs et de propriétés JavaBeans. Architecture Struts 2 1) Le client envoie des requêtes vers un service de l application avec des paramètres éventuels. 2) Le fichier de configuration de l application ou les annotations de classes sont consultés. 3) Les intercepteurs associés à l action sont déclenchés et réalisent les services associés (conservation des paramètres, gestion des sessions, sauvegarde des messages d erreurs ). L intercepteur params assigne les valeurs présentes dans la requête à la classe d action associée par l intermédiaire de ses accesseurs et exécute la méthode de traitement (execute() par défaut). 4) La vue à afficher est sélectionnée en accord avec le fichier de configuration struts.xml ou l annotation correspondante. 5) La classe d action transmet les données nécessaires à la vue. 6) La vue affiche au client les résultats traités

78 Les fichiers de propriétés struts.properties et default.properties Pour la gestion de la configuration de Struts, nous pouvons utiliser les balises <constant/> présentes dans le fichier de configuration struts.xml comme précisé plus haut, ou utiliser un fichier struts.properties. Pour cela, nous devons créer un fichier struts.properties dans le répertoire /WEB INF/src (ou /WEB INF/classes) de l application et surcharger les valeurs par des couples clés/valeurs (principe des fichiers de propriétés Java). Ces valeurs par défaut sont présentes dans le fichier default.properties présent dans le paquetage struts2 core version.jar. Pour reprendre l exemple, nous pouvons passer Struts en mode débogage dans le fichier struts.properties : struts.devmode=true Les valeurs par défaut des propriétés du fichier default.properties sont détaillées ci dessous : struts.i18n.encoding=utf 8 : encodage par défaut utilisé par Struts. struts.objectfactory=spring : l objet factory utilisé par défaut par Struts. struts.objectfactory.spring.autowire=name : la valeur utilisée par défaut quand nous utilisons l objet SpringObjectFactory. Les valeurs possibles sont : name, type, auto et constructor. struts.objectfactory.spring.useclasscache=true : ce paramètre permet d indiquer si les instances de l intégration de Struts Spring doivent être mises en cache. struts.objectfactory.spring.autowire.alwaysrespect=false : ce paramètre permet de gérer la stratégie de l autowire. struts.objecttypedeterminer=notiger : ce paramètre permet de gérer l implémentation de la classe com.opensymphony.xwork2.util.objecttypedeterminer. struts.multipart.parser=jakarta : ce paramètre permet de spécifier le parseur utilisé pour les requêtes multiparts (upload). struts.multipart.savedir : ce paramètre permet de spécifier le répertoire par défaut pour l upload de fichiers. struts.multipart.maxsize= : ce paramètre permet de spécifier la taille maxi pour les fichiers en upload. struts.custom.properties=application,org/apache/struts2/extension/custom : ce paramètre permet de spécifier la liste des fichiers de propriétés qui doit être chargée. struts.mapper.class=org.apache.struts2.dispatcher.mapper.defaultactionmapper : ce paramètre permet de préciser comment les URL sont mappées vers les actions. La valeur par défaut de ce paramètre est : org.apache.struts2.dispatcher.mapper.defaultactionmapper. struts.action.extension=action : ce paramètre permet de gérer par une liste séparée par des virgules, la liste des extensions pour les actions (ex:.action,.do). struts.serve.static=true : ce paramètre indique si Struts doit gérer les contenus statiques présents dans ces fichiers.jar. struts.serve.static.browsercache=true : ce paramètre indique si le filtre utilisé par Struts doit mettre en cache ou pas dans le navigateur du client, les contenus statiques (images, css, javascript ). struts.enable.dynamicmethodinvocation=true : ce paramètre indique si l invocation de méthodes dynamiques est possible avec Struts. Les invocations dynamiques consistent à créer des URI de la forme suivante : ajouter! Client.action. La valeur par défaut est true mais pour des raisons de sécurité, nous pouvons le passer à false si nous n utilisons pas d invocation dynamique. struts.enable.slashesinactionnames=false : ce paramètre indique si les slashs sont autorisés ou non pour les noms des actions. struts.tag.altsyntax=true : ce paramètre indique si les expressions régulières sont autorisées ou non %. struts.devmode=false : ce paramètre indique si nous sommes en mode développement ou pas. Quand ce paramètre a pour valeur true, le fichier de configuration struts.xml est rechargé ainsi que les fichiers de validations et les messages de propriétés (bundles). Ce qui signifie que nous n avons pas besoin de recharger l application à chaque modification d un fichier statique. De même, ce paramètre permet un affichage en mode verbeux pour le débogage. Cependant, en production ce paramètre doit être passé à false pour éviter d afficher les exceptions et alléger la mémoire. struts.i18n.reload=false : ce paramètre permet de préciser si les fichiers de langues (bundles) doivent être rechargés pour chaque requête. Ce principe est très utile en développement mais ne doit pas être utilisé sur un serveur en production. struts.ui.theme=xhtml : ce paramètre permet de préciser le thème utilisé par défaut avec la bibliothèque de tags Struts. struts.ui.templatedir=template : ce paramètre permet de préciser le chemin pour les modèles (templates). struts.ui.templatesuffix=ftl : ce paramètre permet de préciser le type de modèle par défaut. La valeur par défaut - 1 -

79 correspond au modèle proposé par FreeMarker (ftl) mais Velocity (vm) et JSP (jsp) sont aussi disponibles. struts.configuration.xml.reload=false : ce paramètre indique si les fichiers de configuration au format XML doivent être rechargés (struts.xml et les éventuels fichiers inclus). struts.velocity.configfile=velocity.properties : ce paramètre permet de préciser le fichier de configuration par défaut pour le moteur de templates Velocity. struts.velocity.contexts= : ce paramètre permet de déclarer une liste de classes pouvant être utilisée par le contexte Velocity. struts.velocity.toolboxlocation= : ce paramètre permet de déclarer le chemin de la boîte à outils Velocity. struts.url.http.port=80 : ce paramètre permet de préciser le port utilisé pour les requêtes et URL HTTP. struts.url.https.port=443 : ce paramètre permet de préciser le port utilisé pour les requêtes et URL HTTPS. struts.url.includeparams=none : ce paramètre permet de préciser si des paramètres sont inclus dans l URL. struts.custom.i18n.resources=testmessages,testmessages2 : ce paramètre permet de charger les fichiers de messages (bundles) par défaut. struts.dispatcher.parametersworkaround=false : ce paramètre indique si la fonction HttpServletRequest.getParameterMap() doit être activée ou pas. struts.freemarker.manager.classname=org.apache.struts2.views.freemarker.freemarkermanager : ce paramètre permet de préciser la classe à utiliser pour FreeMarker. struts.freemarker.templatescache=false : ce paramètre permet de préciser si le moteur FreeMarker doit utiliser le cache ou pas. struts.freemarker.beanwrappercache=false : ce paramètre permet de préciser si le cache des beans doit être utilisé ou pas avec FreeMarker. struts.freemarker.wrapper.altmap=true : ce paramètre permet de préciser si le mapping est autorisé avec FreeMarker. struts.freemarker.mru.max.strong.size=100 : ce paramètre permet de configurer la taille maximale pour le cache FreeMarker. struts.xslt.nocache=false : ce paramètre permet de spécifier si les résultats XSLT doivent utiliser le cache. struts.mapper.alwaysselectfullnamespace=false : ce paramètre indique si Struts doit utiliser ou non le namespace avant le dernier slash

80 Le fichier de propriétés de l application web.xml Pour la gestion de la configuration de Struts, nous pouvons utiliser les balises <constant/> et le fichier struts.properties. Il existe également une dernière façon de configurer Struts, par l intermédiaire du fichier de description de l application web.xml. Pour cela, nous devons utiliser les constantes d initialisation du filtre avec la syntaxe suivante : Code : /WEB-INF/web.xml <?xml version="1.0" encoding="iso "?> <web-app> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filterdispatcher </filter-class> <init-param> <param-name>struts.devmode</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>... </web-app> - 1 -

81 Le fichier de configuration struts default.xml Dans le fichier struts default.xml présent dans l archive struts2 core version.jar, les résultats par défaut et les intercepteurs sont définis. C est pour cela, que nous pouvons utiliser ce fichier par défaut sans le réécrire dans notre fichier struts.xml afin qu il soit plus petit et lisible. La structure du fichier struts default.xml est la suivante : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <bean class="com.opensymphony.xwork2.objectfactory" name="xwork" /> <bean type="com.opensymphony.xwork2.objectfactory" name="struts" class="org.apache.struts2.impl.strutsobjectfactory" />... <package name="struts-default" abstract="true"> <result-types> <result-type name="chain" class="com.opensymphony.xwork2.actionchainresult"/>... </result-types> <interceptors> <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.aliasinterceptor"/>... </interceptors> <default-interceptor-ref name="defaultstack"/> </package> </struts> - 1 -

82 Une première application Struts Nous allons reprendre notre application de gestion des clients avec l identifiant et le mot de passe pour l adapter à Struts. Arborescence projet exemple02 Le descripteur de déploiement web.xml contient uniquement la déclaration de l utilisation du framework Struts. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="utf-8"?> <web-app id="webapp_9" version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <display-name>struts Blank</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filterdispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> - 1 -

83 Le fichier de configuration du routage struts.xml est présenté ci après. Nous retrouvons les actions possibles qui sont Ajouter_Client.action et ValiderAjouter_Client.action. Nous utilisons également deux constantes pour supprimer les invocations dynamiques et pour passer en mode développement. Nous retrouvons également la définition du paquetage nommé exemple02. L action Ajouter_Client.action réalise une simple redirection vers la vue JSP d ajout. L action ValiderAjouter_Client.action utilise la classe Client, mais comme le paramètre method n est pas défini dans l action, c est la méthode execute() de cette classe qui sera appelée par défaut. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple02" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple02.client"> <result>/jsp/afficherclient.jsp</result> </action> </package> </struts> Désormais, nous pouvons présenter la classe d action nommée Client. Cette classe utilise les deux propriétés du formulaire de saisie que sont identifiant et motdepasse. Ce fichier est une simple classe POJO. Chaque propriété possède des accesseurs qui seront automatiquement renseignés lors de l appel de l action grâce à l intercepteur params. Ainsi, les méthodes setidentifiant() et setmotdepasse() seront déclenchées et affectées par Struts. Code : exemple02.client.java package exemple02; import public class Client implements Serializable private String identifiant; private String motdepasse; public String getidentifiant() return identifiant; public void setidentifiant(string identifiant) this.identifiant = identifiant; public String getmotdepasse() return motdepasse; public void setmotdepasse(string motdepasse) this.motdepasse = motdepasse; public String execute() System.out.println("Dans la méthode de l action"); return "success"; - 2 -

84 Grâce à Struts, plus besoin de gérer les récupérations de paramètres dans la requête (request.getparameter() ou request.getattribute()), ni de gérer les renvois des paramètres dans la réponse (request.setattribute()). Le simple fait d utiliser les accesseurs de chaque propriété permet de gérer les paramètres en globalité. La page JSP /jsp/ajouterclient.jsp possède la structure simple suivante : Code : /jsp/afficherclient.jsp <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <form method="post" action="validerajouter_client.action"> <table> <tr> <td>identifiant:</td> <td><input type="text" name="identifiant"/></td> </tr> <tr> <td>mot de passe:</td> <td><input type="text" name="motdepasse"/></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="ajouter le client"/></td> </tr> </table> </form> </div> </body> </html> Enfin, le fichier JSP /jsp/afficherclient.jsp possède la structure suivante : Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>afficher le client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <p> <h4>informations sur le client:</h4> Identifiant: <s:property value="identifiant"/><br/> Mot de passe: <s:property value="motdepasse"/><br/> </p> </div> </body> </html> Nous remarquons l utilisation de la bibliothèque de tags Struts avec la ligne suivante : <%@ taglib prefix="s" uri="/struts-tags" %> Enfin, chaque propriété est accessible avec la balise Struts <s:property/> grâce aux getters de la classe d action. <s:property value="identifiant"/> Nous pouvons utiliser cette application, avec l URL suivante afin d ajouter un client :

85 Formulaire de saisie exemple02 Affichage du client exemple02 Notre première application Struts est opérationnelle. Elle permet de gérer deux attributs présents dans un formulaire. La simple déclaration des accesseurs permet de gérer toute la chaîne de développement (requête/réponse). Pour s en convaincre, nous pouvons juste supprimer le setter setmodepasse() dans la classe Client et constater le résultat. Affichage du client sans le setter sur le mot de passe - 4 -

86 - 5 -

87 En résumé Ce chapitre a présenté le fonctionnement général de Struts avec les intercepteurs, le fichier de configuration de l application struts.xml et les différentes balises XML de déclaration. Les fichiers de configuration du framework sont ensuite détaillés et un premier exemple de gestion des clients est largement expliqué

88 Présentation Le chapitre précédent a introduit un premier projet simple Struts. La méthode execute() de la classe d action Client affiche une trace dans la console Java à l aide de la méthode System.out.println(). Code : exemple02.client.java package exemple02; import public class Client implements Serializable private String identifiant; private String motdepasse;... public String execute() System.out.println("Dans la méthode de l action"); return "success"; Trace Java Nous remarquons que Struts est très verbeux, il affiche plusieurs traces de warning pour une simple application. Le framework repose sur l interface de journalisation Log4J ( et peut donc utiliser un fichier de configuration afin de supprimer ces traces ou les adapter selon nos besoins. Ces traces correspondent aux outils FreeMarker et XWork. Pour les supprimer, il suffit de créer un fichier nommé log4j.properties dans le répertoire /WEB INF/src (ou /WEB INF/classes) de l application au même niveau que le fichier de gestion struts.xml. Le fichier de configuration le plus simple pour stopper les traces est le suivant : Code : exemple02.log4j.properties #définition du niveau et des Appender du rootlogger (ordre : DEBUG - INFO - WARN - ERROR - FATAL) log4j.logger.freemarker=off log4j.logger.com.opensymphony.xwork2=off log4j.logger.org.apache=info log4j.rootlogger=debug - 1 -

89 Arborescence et fichier log4j Ensuite, il est nécessaire de copier l archive de Log4J au format.jar, log4j version.jar dans le répertoire des librairies de l application /WEB INF/lib. Nous pouvons désormais relancer le projet et constater la trace de journalisation dans la console Java. Trace de la console Java avec Log4J Nous pouvons afficher à nouveau notre formulaire d ajout d un client et valider l action pour visualiser la trace sans journalisation. Trace de la console Java sans Log4J - 2 -

90 Gestion du débogage Le débogage est désormais une opération facile avec Struts. En effet, le framework propose une balise XHTML <s:debug/> pour afficher les traces avec des informations sur les paramètres, la session en cours ou encore les objets. La balise <s:debug/> peut être placée dans n importe quelle page JSP. Cette balise possède un seul paramètre optionnel nommé id. Nous allons reprendre notre page AfficherClient.jsp et ajouter cette balise en début de fichier. Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>afficher le client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> </html> Désormais, un clic sur le lien nommé [debug] permet de visualiser la pile de traces des objets présents dans le contexte de l application. Cette balise permet de facilement déboguer une application et également de visualiser les propriétés de l action et le contenu des objets présents en session ou dans la collection de l application. Balise de débobage <s:debug/> - 1 -

91 Affichage des traces de débogage Le débogage est réalisé avec Struts par l intermédiaire de l intercepteur debugging. Cet intercepteur est présent dans la configuration par défaut de Struts. Nous pouvons déclencher cet intercepteur directement à partir d une URL en utilisant le paramètre debug=xml ou debug=console. Le paramètre debug=xml permet d afficher un arbre XML contenant la liste des valeurs de traces et les objets. Voici un exemple avec le déclenchement de l URL suivante :

92 Affichage de l arbre de débogage XML Enfin, la trace en mode console permet d afficher une fenêtre avec des informations sur l application en cours. Dans cette console, nous pouvons saisir des expressions Object Graph Navigation Language (OGNL) pour afficher les paramètres de la page

93 Console de débogage OGNL Cette console n est actuellement pas opérationnelle avec le navigateur Internet Explorer mais fonctionne sous Firefox

94 Gestion du profilage (Profiling) Struts fournit en standard un outil de gestion du profilage pour nos applications. Le profilage permet de donner des informations précises sur le déroulement d une action. Le profilage permet par exemple d améliorer le code ou de trouver une partie de code particulièrement lente lors de son exécution. Struts fournit des traces sur le temps d exécution des différentes actions, de ses filtres, intercepteurs et résultats. Chacun de ces services utilise la classe UtilTimeStack présente dans le paquetage com.opensymphony.xwork2.util.profiling. Par défaut, les traces ne sont pas affichées. Par contre, si les traces sont activées pour une action par exemple, le résultat est affiché par un logger dans la console Java ou dans le fichier de log du serveur Java EE (catalina.out pour Tomcat). Pour activer le profilage avec Struts, il existe plusieurs techniques : 1) Lancer une requête avec le paramètre profiling=true ou profiling=yes dans l URL de l application. Pour stopper le profilage d une application, il suffit de passer le paramètre profiling=no ou profiling=false 2) Activer la méthode UtilTimerStack.setActive(true) dans la fonction à profiler. Code : exemple02.client.java package exemple02; import java.io.serializable; import public class Client implements Serializable... public String execute() UtilTimerStack.setActive(true); System.out.println("Dans la méthode de l action"); return "success"; 3) Activer la constante système UtilTimerStack.ACTIVATE_PROPERTY. Code : exemple02.client.java package exemple02; import java.io.serializable; import public class Client implements Serializable... public String execute() System.setProperty(UtilTimerStack.ACTIVATE_PROPERTY,"true"); System.out.println("Dans la méthode de l action"); return "success"; Pour activer le profilage d une application Struts, celui ci doit être paramétré en mode développement struts.devmode=true. Le profilage ne peut être affiché que lorsque les traces sont activées et pas interdites par Log4J et son fichier de propriétés. Voici un exemple de trace avec l URL suivante : profiling=true INFO: [500ms] - Handling request from Dispatcher [0ms] - create DefaultActionProxy: - 1 -

95 [0ms] - actioncreate: ValiderAjouter_Client [453ms] - invoke: [453ms] - interceptor: exception [453ms] - invoke: [453ms] - interceptor: alias [453ms] - invoke: [453ms] - interceptor: servletconfig [453ms] - invoke: [453ms] - interceptor: i18n [453ms] - invoke: [453ms] - interceptor: prepare [453ms] - invoke: [453ms] - interceptor: staticparams [453ms] - invoke: [453ms] - interceptor: actionmappingparams [453ms] - invoke: [453ms] - interceptor: params [453ms] - invoke: [453ms] - interceptor: conversionerror [453ms] - invoke: [453ms] - interceptor: validation [125ms] - invoke: [125ms] - interceptor: workflow [125ms] - invoke: [0ms] - invokeaction: ValiderAjouter_Client [125ms] - executeresult: success Nous pouvons également profiler des activités spécifiques avec les methodes push() et pop() de la classe statique UtilTimerStack. Voici un exemple pour tracer uniquement les accès à une base de données. Code : exemple02.client.java package exemple02; import com.opensymphony.xwork2.actionsupport; import public class Client extends ActionSupport... public String execute() String activite="database access"; UtilTimerStack.push(activite); try System.out.println("Dans la méthode de l action et l accès à la base de données"); catch(exception e) UtilTimerStack.pop(activite); finally UtilTimerStack.pop(activite); return "success"; - 2 -

96 En résumé Ce chapitre a présenté dans un premier temps la gestion des traces avec le framework Struts. Struts utilise un système d affichage très verbeux qui doit être correctement paramétré lors des développements. Dans un deuxième temps, nous avons présenté la balise XHTML <s:debug/> fournie en standard avec la bibliothèque de tags Struts. Enfin, le dernier paragraphe explique la mise en place du profilage lors des développements avec l outil Struts

97 Présentation Struts utilise un contrôleur principal pour la gestion du routage et des paramètres. Nous pouvons ainsi concentrer nos efforts à la réalisation et au codage des différentes actions du projet. Chaque action Struts est définie dans le fichier de configuration struts.xml (ou dans les classes avec la technique zéro configuration). Struts permet également l utilisation d expressions régulières et les invocations dynamiques de méthodes

98 Classes d action Les créations d actions sont le plus important travail à réaliser lors de développements à l aide du framework Struts. Nous aurons par exemple des actions pour afficher la page d accueil, gérer des articles, s occuper de l authentification client ou autre. Certaines opérations sont simples et ne requièrent pas de classe mais réalisent simplement une redirection alors que d autres effectuent des traitements complexes associés à une classe d action. Une classe d action est tout simplement une classe Java. Cette classe contient donc des attributs et des méthodes. Cependant quelques règles doivent être respectées pour avoir une véritable classe d action : Chaque attribut doit être associé à ses accesseurs (getter et setter). Les règles sont les mêmes que celles des JavaBeans. Une classe d action doit avoir un constructeur par défaut sans argument. Cependant, si nous ne créons pas de constructeur par défaut, Struts va le faire pour nous lors de la compilation. Nous pouvons également utiliser un autre constructeur (par initialisation) mais dans ce cas, le constructeur par défaut reste obligatoire. Une classe d action doit avoir au moins une méthode pour réaliser l action. Par défaut, cette méthode se nomme execute() mais peut très bien porter n importe quel nom en association avec la balise <action/> et l attribut method présents dans le fichier de configuration de l action struts.xml. Une classe d action peut être associée à plusieurs méthodes. Dans ce cas, la classe d action va par exemple gérer l affichage des clients, le formulaire de création, la validation de la création, la consultation, l affichage du formulaire de modification, la validation de la modification et enfin la suppression. Une classe d action n a pas besoin d hériter d une classe spécifique ou d une interface particulière. Malgré tout, il est plus simple et conseillé d hériter de la classe ActionSupport présente dans le paquetage com.opensymphony.xwork2.action. La classe com.opensymphony.xwork2.actionsupport est la classe d action par défaut de Struts. Le framework crée une instance de cette classe si aucune déclaration n est précisée. Si nous implémentons la classe ActionSupport, les constantes suivantes sont alors disponibles : SUCCESS : cette constante indique que l exécution de l action est correcte et que la page de succès adaptée doit être affichée. NONE : cette constante indique que l exécution de l action est correcte mais qu aucun résultat ne doit être retourné. ERROR : cette constante indique que l exécution de l action est incorrecte et que l utilisateur doit être redirigé vers une page d erreur. INPUT : cette constante indique une erreur de validation des entrées ou saisies utilisateur. Elle précise généralement que la page de saisie doit être à nouveau affichée sans perte des données. LOGIN : cette constante indique que l action ne peut être exécutée car l utilisateur n est pas authentifié et force l affichage de la page d identification. Ces valeurs sont donc utilisées en remplacement des chaînes de caractères success, input De plus, l héritage de la classe ActionSupport permet également de surcharger plusieurs méthodes comme la fonction validate() pour réaliser des validations utilisateur en programmation. Un des principaux avantages d utiliser Struts est sa gestion des persistances d attributs avec la simple déclaration dans les classes et les accesseurs. Ceci est possible grâce à l intercepteur params qui gère la pile des données. Cette pile de données est accessible dans les pages JSP. Code : exemple02.client.java package exemple02; import com.opensymphony.xwork2.actionsupport; import com.opensymphony.xwork2.util.profiling.utiltimerstack; - 1 -

99 @SuppressWarnings("serial") public class Client extends ActionSupport private String identifiant; private String motdepasse; public Client() // getter identifiant public String getidentifiant() return identifiant; // setter identifiant public void setidentifiant(string identifiant) this.identifiant = identifiant; // getter motdepasse public String getmotdepasse() return motdepasse; // setter motdepasse public void setmotdepasse(string motdepasse) this.motdepasse = motdepasse; public String execute() System.out.println("Dans la méthode de l action"); return "success"; Depuis que les classes d action Struts sont devenues des classes POJO, la réalisation de tests ou l utilisation des classes est relativement simple. Nous pouvons instancier les classes et accéder directement aux propriétés par l intermédiaire des accesseurs. Voici un exemple de code pour l utilisation de la classe Client.java. Client client=new Client() ; client.setidentifiant("jlafosse"); client.setmotdepasse("jerome"); String resultat=client.execute(); if(resultat.equals("success")) System.out.println("Succès"); else System.out.println("Erreur"); - 2 -

100 Gestion des ressources Les classes d action permettent des accès aux ressources externes comme les Servlets Java. Les ressources externes sont les suivantes : la classe ServletContext ; la classe HttpSession ; la classe HttpServletRequest ; la classe HttpServletResponse. Struts propose d accéder à ces ressources en utilisant soit l héritage de classes, soit l implémentation d interfaces. 1. Accès aux ressources par classe Il existe deux classes qui permettent d accéder aux ressources, com.opensymphony.xwork2.actioncontext et org.apache.struts2.servletactioncontext. La classe statique ServletActionContext offre trois méthodes statiques getrequest(), getresponse() et getservletcontext() pour accéder aux ressources. Un point très important de l utilisation de la classe statique ServletActionContext est sa portée. En effet, il est impossible d utiliser cette classe dans un constructeur d une classe d action. Dans le constructeur, la classe statique est encore inconnue par la classe d action car elle n a pas encore été passée à celui ci. Voici un petit exemple d utilisation de la classe statique ServletActionContext dans une méthode d action. Code : exemple02.client.java package exemple02; import javax.servlet.servletcontext; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpsession; import org.apache.struts2.servletactioncontext; import com.opensymphony.xwork2.actionsupport; import public class Client extends ActionSupport... public String execute() HttpServletRequest request=servletactioncontext.getrequest(); HttpSession session=request.getsession(); if(session.getattribute("client")==null) System.out.println("Pas de client dans la session"); else System.out.println("Un client dans la session"); ServletContext context=servletactioncontext.getservletcontext(); System.out.println("Un attribut dans le contexte de l application : "+context.getinitparameter(" ")); - 1 -

101 return "success"; Code : /WEB-INF/web.xml <?xml version="1.0" encoding="utf-8"?> <web-app id="webapp_9" version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <!-- Parametres globaux --> <context-param> <param-name> </param-name> <param-value>info@monsite.com</param-value> </context-param> </web-app> Accès aux ressources 2. Accès aux ressources par interface Le framework fournit quatre interfaces pour accéder aux ressources externes : org.apache.struts2.util.servletcontextaware, org.apache.struts2.interceptor.servletrequestaware, org.apache.struts2.interceptor.servletresponseaware, org.apache.struts2.interceptor.sessionaware. L interface ServletContextAware possède une méthode nommée setservletcontext() permettant d accéder à l objet ServletContext dans la classe d action. Lorsqu une action est déclenchée, le framework vérifie si l interface est implémentée et si une fonction surcharge la déclaration. À l intérieur de cette méthode, nous devons assigner une variable servletcontext pour récupérer l objet. L interface ServletRequestAware possède une méthode nommée setservletrequest() permettant d accéder à l objet HttpServletRequest dans la classe d action. Lorsqu une action est déclenchée, le framework vérifie si l interface est implémentée et si une fonction surchage la déclaration. À l intérieur de cette méthode, nous devons assigner une variable request pour récupérer l objet. L interface ServletResponseAware possède une méthode nommée setservletresponse() permettant d accéder à l objet HttpServletResponse dans la classe d action. Lorsqu une action est déclenchée, le framework vérifie si l interface est implémentée et si une fonction surcharge la déclaration. À l intérieur de cette méthode, nous devons assigner une variable response pour récupérer l objet. L interface SessionAware possède une méthode nommée setsession() permettant d accéder à l objet HttpSession dans la classe d action. Lorsqu une action est déclenchée, le framework vérifie si l interface est implémentée et si une fonction surcharge la déclaration. À l intérieur de cette méthode, nous devons assigner une variable session pour récupérer la collection des attributs présents dans la session. Struts passe en effet une instance de la classe org.apache.struts2.dispatcher.sessionmap en paramètre. Cette classe hérite de la classe AbstractMap qui elle même implémente la classe Map. La classe SessionMap fournit plusieurs - 2 -

102 méthodes pouvant manipuler les objets de type HttpSession. Les méthodes proposées par la classe SessionMap sont les suivantes : invalidate() : cette méthode permet de supprimer la session. clear() : cette méthode permet d enlever tous les attributs de l objet HttpSession. get(cle) : cette méthode retourne un attribut précisé par la clé en paramètre. Cette méthode retourne null si l objet n existe pas ou n a pas été trouvé. put(cle, valeur) : cette méthode permet de sauvegarder un attribut dans la session. Si l objet HttpSession est null, un nouvel objet HttpSession va alors être créé. remove(cle) : cette méthode permet de supprimer un attribut spécifique dans la session. L exemple suivant permet de mettre en application l accès aux ressources externes en utilisant les interfaces proposées par Struts. L exemple repose sur le même principe que le projet exemple02. Le fichier de gestion de l application struts.xml est le suivant : Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple03" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple02.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> <action name="supprimer_client" class="exemple02.client" method="supprimer"> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> L action Ajouter_Client permet d afficher uniquement le formulaire JSP d ajout d un nouveau client. L action ValiderAjouter_Client permet d ajouter les informations du client en session si les saisies ne sont pas vides. Dans le cas contraire, l internaute est redirigé vers la page de saisie. Enfin, l action Supprimer_Client permet de supprimer les informations du client dans la session. Les expressions JSP Expression Language $identifiant et $motdepasse présentes dans l attribut value des balises <input/> permettent de conserver les saisies de l utilisateur. Code : /jsp/ajouterclient.jsp <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> - 3 -

103 <h3>ajouter un client</h3> <form method="post" action="validerajouter_client.action"> <table> <tr> <td>identifiant:</td> <td><input type="text" name="identifiant" value="$ identifiant"/></td> </tr> <tr> <td>mot de passe:</td> <td><input type="text" name="motdepasse" value="$ motdepasse"/></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="ajouter le client"/></td> </tr> </table> </form> </div> </body> </html> La page suivante permet d afficher les valeurs des attributs présents dans la session que sont identifiantsession et motdepassesession. Le lien proposé déclenche la méthode supprimer() de la classe d action afin de supprimer la session. Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>afficher le client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4>informations sur le client:</h4> Identifiant: <s:property value="#session.identifiantsession"/><br/> Mot de passe: <s:property value="#session.motdepassesession"/><br/> </p> <a href="supprimer_client.action">supprimer le client</a> </div> </body> </html> Enfin, la classe Client permet de gérer les attributs de l utilisateur dans la session lors d une authentification. La classe Client accède à la session utilisateur car l interface SessionAware est implémentée et la méthode setsession (Map map) est surchargée. L action ajouter() permet d enregistrer les paramètres du client dans la session et la méthode supprimer() permet d enlever ces mêmes informations de la session. Une vérification des saisies (non vides) est également utilisée dans cette classe. Nous pouvons tester l application exemple03 avec ce lien en essayant des saisies vides et des saisies complètes. Enfin, nous pouvons supprimer les attributs dans la session en utilisant le lien adapté

104 Affichage des informations client exemple03 3. Passage de paramètres Les déclarations d action peuvent également recevoir des paramètres statiques dans le fichier de configuration struts.xml. Cette technique permet d affecter des valeurs à des propriétés de la classe. Si nous reprenons notre exemple précédent, nous pouvons ajouter une balise <param/> dans la déclaration de l action. Chaque paramètre correspond à une propriété de la classe d action (setter et getter). L intercepteur staticparams s occupe alors du mapping entre les paramètres déclarés dans le fichier struts.xml et le code de l action. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts>... <action name="validerajouter_client" class="exemple03.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> <param name="page">frontoffice</param> </action>... </struts> Code : exemple03.client.java package exemple03; import java.util.map; import org.apache.struts2.dispatcher.sessionmap; import org.apache.struts2.interceptor.sessionaware; import public class Client extends ActionSupport implements SessionAware private String identifiant; private String motdepasse; private String page;... public String getpage() return page; - 5 -

105 ... public void setpage(string page) this.page = page; Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>afficher le client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4>informations sur le client:</h4> Identifiant: <s:property value="#session.identifiantsession"/><br/> Mot de passe: <s:property value="#session.motdepassesession"/><br/> Page: <s:property value="page"/><br/> </p> <a href="supprimer_client.action">supprimer le client</a> </div> </body> </html> Utilisation des paramètres d action exemple03-6 -

106 Gestion dynamique du mapping Un projet final contient plusieurs déclarations d actions et limite parfois la lisibilité et la maintenabilité du fichier de configuration struts.xml. Dans la première version de Struts, le fichier de configuration pouvait contenir beaucoup de lignes et parfois des déclarations quasiment identiques (ex : gestion des articles, des clients, des catégories ). Afin d apporter des solutions à ces problèmes, Struts propose désormais des déclarations d actions sous forme d expressions régulières ou modèles appelés wildcard. Nous pouvons utiliser un nouveau projet exemple04 et ajouter une déclaration dynamique pour le formulaire de création afin de l afficher quel que soit le préfixe saisit avant la phrase _Client.action. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts>... <action name="*_client"> <result>/jsp/ajouterclient.jsp</result> </action>... </struts> Le caractère * permet d attraper les URLs composées de n importe quelle chaîne de caractères. Nous pouvons ainsi déclencher l URL suivante /exemple04/Creer_Client.action pour afficher le formulaire en création. La partie de l URL qui est attrapée par le caractère * est disponible avec le terme 1. Si nous utilisons plusieurs caractères d échappement, il existe autant de paramètres que de caractères d échappement (1, 2, ). Nous pouvons ainsi considérablement réduire le code de notre exemple en utilisant les échappements et l invocation dynamique de méthodes. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> <action name="*_client" class="exemple04.client" method="1"> <result name="input">/jsp/client/ajouter.jsp</result> <result name="success">/jsp/client/1.jsp</result> </action> </package> </struts> Ainsi, l URL va déclencher l action nommée *_Client et la méthode Ajouter() de la classe exemple04.client. De même, l URL va déclencher l action nommée *_Client et la méthode ValiderAjouter() de la classe exemple04.client. Chaque méthode de la classe Client retourne, en cas de succès, vers les pages JSP du même nom. Nous retrouvons alors les vues JSP /jsp/client/ajouter.jsp et /jsp/client/validerajouter.jsp

107 Arborescence projet exemple04 Cet exemple, bien que très utile ne représente pas un véritable projet de plate forme web. Si nous étudions par exemple, une configuration d un système de gestion de client, nous retrouvons les actions suivantes à déclarer : Consulter un client. Afficher le formulaire de création d un client. Valider la création d un client. Afficher le formulaire en modification d un client. Valider la modification d un client. Supprimer un client. Sans l utilisation d invocations dynamiques, voici un exemple du fichier de configuration pour la gestion des clients. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="backoffice" namespace="/admin" extends="strutsdefault"> <action name="lister_client" class="exemple.client" method="lister"> <result>/vues/administrateurs/client/listerclient.jsp</result> </action> <action name="editer_client" class="exemple.client" method="editer"> <result>/vues/administrateurs/client/editerclient.jsp</result> </action> <action name="ajoutermodifier_client" class="exemple.client" method="ajoutermodifier"> <result name="listerclient" type="redirectaction">lister_client</result> <result name="input">/vues/administrateurs/client/editerclient.jsp</result > </action> - 2 -

108 <action name="consulter_client" class="exemple.client" method="consulter"> <result>/vues/administrateurs/client/consulterclient.jsp</result> </action> <action name="supprimer_client" class="exemple.client" method="supprimer"> <result name="listerclient" type="redirectaction">lister_client</result> </action> </package> </struts> Bien entendu, ce code doit être dupliqué pour la gestion des articles par exemple.... <action name="lister_article" class="exemple.article" method="lister"> <result>/vues/administrateurs/article/listerarticle.jsp</result> </action> <action name="editer_article" class="exemple.article" method="editer"> <result>/vues/administrateurs/article/editerarticle.jsp</result> </action>... Nous remarquons tout de suite les répétitions de déclarations dans le fichier de configuration et la lourdeur du code au niveau du balisage. Avec l utilisation des invocations dynamiques de méthodes, nous pouvons remplacer tout ceci par le code ci dessous :... <action name="*_*" class="exemple.2action" method="1"> <result name="lister2" type="redirectaction">lister_2</result> <result name="input">/vues/administrateurs/ 2/editer2.jsp</result> <result>/vues/administrateurs/2/12.jsp</result> </action>... Ces cinq lignes de déclarations génériques remplacent alors des dizaines de lignes de code. Le fonctionnement sera identique et totalement opérationnel pour une gestion de clients, d articles ou encore de catégories. Cette technique est très utile mais demande une certaine rigueur dans la structure du site. Arborescence générique Classes d action génériques - 3 -

109 Dans cet exemple, tout est dynamique que ce soit l appel des classes avec le paramètre 2, l appel des méthodes de classes avec le paramètre 1 et l invocation des pages JSP avec la combinaison des deux paramètres. Le paramètre 0 contient l URI reçu par l action

110 Invocation dynamique de méthodes Il existe avec Struts un caractère spécial qui peut être utilisé pour l invocation dynamique de méthodes. Ce caractère! est appelé notation bang. Par défaut, l invocation dynamique de méthodes est activée dans le fichier de propriétés default.properties. La déclaration est la suivante : struts.enable.dynamicmethodinvocation = true Cette déclaration peut également être spécifiée dans le fichier XML struts.xml avec la balise adaptée : Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="true" />... </struts> Pour pouvoir utiliser l invocation dynamique de méthodes il est impératif de déclarer le paramètre value à true si cette balise est utilisée dans le fichier de configuration de l application. L utilisation de l invocation dynamique de méthodes n est pas recommandée dans une application professionnelle pour des raisons évidentes de sécurité. En effet, les URLs peuvent être complexes et sous différentes formes, il devient alors très difficile de réaliser tous les tests de vérification. Enfin, le référencement effectué par les principaux moteurs de recherches sur les URLs par invocation dynamique est très mauvais. Pour désactiver ce service, nous pouvons utiliser notre fichier struts.xml et la déclaration suivante : <constant name="struts.enable.dynamicmethodinvocation" value="false" /> Nous pouvons reprendre le projet exemple04 et utiliser l invocation dynamique de méthodes. Pour cela, le fichier struts.xml est adapté en conséquence ainsi que les différents liens de l application. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="true" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="struts-default"> <default-action-ref name="gestion_client" /> <action name="gestion_client" class="exemple04.client"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="ajouter">/jsp/ajouterclient.jsp</result> <result name="afficher">/jsp/afficherclient.jsp</result> </action> </package> </struts> Code : exemple04.client.java package exemple04; import public class Client extends ActionSupport private String identifiant; - 1 -

111 private String motdepasse; public Client()... // Afficher le formulaire d édition (ajout ou modification) public String ajouter() return "ajouter"; // ajouter les informations du client dans la session public String validerajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; Code : /jsp/ajouterclient.jsp <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <form method="post" action="gestion_client! validerajouter.action">... </form> </div> </body> </html> Arborescence exemple04 avec invocation dynamique - 2 -

112 Formulaire d ajout avec invocation dynamique Affichage du formulaire avec invocation dynamique - 3 -

113 Gestion des résultats Les classes d action Struts retournent des chaînes de caractères en rapport avec le traitement de l action. Par exemple, si l action contient les résultats success, input et error, la déclaration de l action dans le fichier struts.xml doit avoir trois résultats avec les valeurs des attributs name suivants : <action name="monaction" class="nompaquetage.nomclasse"> <result name="success">...</result> <result name="input">...</result> <param name="error">...</param> </action> Un résultat est représenté par l intermédiaire de la balise <result/> et de ses deux attributs que sont name et type. L attribut name permet de donner un nom au résultat en correspondance à la valeur return de l action. La valeur par défaut de cet attribut est success. L attribut type permet de définir le type de résultat. La valeur par défaut de cet attribut est dispatcher (redirection simple). Par exemple, les déclarations suivantes sont identiques : <result name="success" type="dispatcher">/jsp/afficherclient.jsp </result> <result name="success">/jsp/afficherclient.jsp</result> <result>/jsp/afficherclient.jsp</result> Le type dispatcher, correspondant à une redirection vers une page JSP dans la majorité des cas, est le type le plus utilisé. La liste proposée ci dessous présente tous les types de résultats possibles que nous pouvons indiquer dans l attribut type de la balise <result/>. En plus de ces différents types de résultats, le développeur peut utiliser des plug ins avec des résultats spécifiques ou encore développer ses propres plug ins avec des résultats adaptés. Nous verrons dans le chapitre Plug ins Struts la création de plug ins avec Struts. dispatcher : ce type permet de rediriger l action vers une page JSP de résultat. redirect : ce type permet de réaliser une redirection complète vers une autre URL. La différence entre ce type et dispatcher concerne la forme de redirection. Avec dispatcher l URL du navigateur n est pas changée et les paramètres présents dans la requête sont conservés. Avec redirect l URL du navigateur est changée dans le navigateur et les paramètres présents dans la requête sont perdus. redirectaction : ce type permet de réaliser une redirection vers une autre action. chain : ce type permet d utiliser le chaînage des actions (plusieurs actions liées). freemarker : ce type permet d utiliser le moteur de templates FreeMarker. velocity : ce type permet d utiliser le moteur de templates Velocity. httpheader : ce type permet de retourner des en têtes HTTP spécifiques au navigateur. stream : ce type permet de retourner un résultat sous la forme d un flux InputStream vers le navigateur (images, vidéos ). xslt : ce type permet d utiliser des résultats XML et des feuilles de styles XSLT. plaintext : ce type permet de retourner le résultat sous forme de texte pour afficher le code source d une page. 1. Redirection avec paramètres Le type dispatcher est le plus utilisé dans les résultats. Ce type permet de réaliser une redirection vers une ressource - 1 -

114 comme une page JSP (ou une page HTML/XHTML) sans perte de paramètres. Ce paramètre doit indiquer une ressource interne au projet mais ne permet pas une redirection vers une ressource externe ou une URL absolue (ex : <action name="supprimer_client" class="exemple03.client" method="supprimer"> <result name="success" type="dispatcher">/jsp/afficherclient.jsp</result> </action> 2. Redirection sans paramètre Le type redirect permet de réaliser une redirection vers une ressource interne ou externe mais avec perte de paramètres. Le principal intérêt d utiliser ce type de résultat est de rediriger l utilisateur vers une ressource externe. Ce type de redirection est également plus rapide. Enfin, ce type de redirection est très utilisé pour gérer le double envoi (double submit). En effet, avec une redirection simple, lorsqu un utilisateur ajoute par exemple un produit à son panier, si pour n importe qu elle raison, il actualise son navigateur, un second article est ajouté. La redirection sans paramètre permet de supprimer les paramètres de la requête et d emmener l utilisateur vers une autre URL. Nous pouvons également passer des paramètres à une redirection en utilisant des propriétés. La syntaxe $propriete permet de passer des données présentes dans la classe d action avec ses accesseurs. <result name="success" type="redirect">/jsp/afficherclient.jsp? identifiant=$identifiant</result> Si nous prenons un nouveau projet nommé exemple04, nous allons réaliser une première redirection vers le moteur de recherche google et une seconde vers une action du site. Pour réaliser une redirection vers une ressource externe, il est nécessaire d utiliser le type redirect. Le fichier de configuration de l application struts.xml est présenté ci dessous : Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success" type="redirect">/jsp/afficherclient.jsp?identifiant=$ identifiant</result> </action> <action name="google"> <result type="redirect"> </action> </package> </struts> Le fichier de l action simple permet de gérer uniquement des réponses. Code : exemple04.client.java package exemple04; import com.opensymphony.xwork2.actionsupport; - 2 -

115 @SuppressWarnings("serial") public class Client extends ActionSupport private String identifiant; private String motdepasse; public Client() //setter et getter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "success"; Enfin, la page d affichage du client permet de retourner les valeurs des attributs. Nous pouvons remarquer que les valeurs sont perdues mais que le paramètre identifiant est bien retourné. Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>afficher le client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4>informations sur le client:</h4> Identifiant: <s:property value="identifiant"/> <%=request.getparameter("identifiant") %><br/> Mot de passe: <s:property value="motdepasse"/><br/> <a href="../google.action">google</a> </p> </div> </body> </html> Le projet exemple04 peut être testé à cette adresse :

116 Formulaire d ajout d un nouveau client exemple04 Affichage des informations du client exemple04 3. Redirection vers action Le type redirectaction est utilisé pour réaliser une redirection vers une autre action à l image du type redirect. Le nom de l action est précisé dans la balise <result/> sans extension (.action). La redirection suivante permet de rediriger le projet exemple04 vers une autre action avec perte de paramètre. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="struts-default"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> - 4 -

117 </action> <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success" type="redirectaction">afficher_client</result> </action> <action name="afficher_client" class="exemple04.client"> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> La validation de l ajout du client redirige l utilisateur vers l action Afficher_Client. Formulaire d ajout d un client exemple04 Affichage du client avec redirection exemple04 L extension de l action (.action) n a pas besoin d être précisée, le suffixe n est pas nécessaire. Cette technique permet d utiliser des noms d extensions au choix (.action,.do )

118 Nous pouvons également réaliser une redirection avec passage de paramètres sur le même principe que précédemment. Les paramètres sont alors ajoutés à la requête et passés dans l URL ce qui réduit les possibilités de typage. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts>... <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success" type="redirectaction"> <param name="actionname">afficher_client</param> <param name="identifiant">$identifiant</param> </result> </action>... </struts> Redirection avec paramètres 4. Redirection chaînée Le type chain permet de réaliser des actions chaînées, c est à dire une redirection vers une autre action avec la conservation de l état de l action d origine dans la cible. Nous pouvons également chaîner des actions vers un autre paquetage Struts 2 en précisant le nom de celui ci. Nous allons reprendre l exemple précédent et chaîner la redirection de la validation de l ajout vers l affichage. Si une action est chaînée avec une autre action, la première action va stocker la valeur de la pile d exécution et la transmettre à la seconde action et ainsi de suite. Ce type de redirection permet de réaliser un routage sans perte de paramètre. Dans l exemple précédent, le chaînage permet de ne pas perdre les informations saisies par l utilisateur. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="struts

119 default"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success" type="chain">afficher_client</result> </action> <action name="afficher_client" class="exemple04.client"> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> Affichage de paramètres avec redirection chaînée En général, l utilisation des redirections chaînées n est pas très recommandée. Si une action doit appeler une autre action, cela est réalisé avec une redirection en retour de la première action ou alors le code de la seconde action peut être appelé dans la première. 5. FreeMarker et Velocity Ces types de résultats sont utilisés pour les présentations (templates) et modèles d affichage. Le chapitre Les moteurs de templates explique en détail ces deux types de résultats. 6. HttpHeader Ce type de résultat est utilisé pour renvoyer une réponse HTTP au navigateur. Les codes de statut HTTP permettent au navigateur d obtenir des informations de la part du serveur. Par exemple, le code 404 indique au navigateur que la ressource n a pu être trouvée. Le code 500 indique une erreur interne relative au serveur. Nous pouvons par exemple renvoyer une réponse 403 au navigateur lors de l authentification afin d indiquer que l accès à cette ressource est interdit

120 Types de réponses HTTP Grâce à cette technique, Struts va pouvoir générer des réponses spécifiques. Si les pages 403 sont gérées par le descripteur de déploiement web.xml, seule cette page sera affichée. Code : /WEB-INF/web.xml <error-page> <error-code>403</error-code> <location>/403.html</location> </error-page> 7. Stream Ce type de résultat représenté par un flux d octets est utilisé pour les images, vidéos ou autre. Le chapitre Plug ins Struts consacré au plug in JFreeChart explique en détail ce type de résultat. 8. XSLT Ce type de résultat représenté par des informations au format XML est présenté en détail dans le chapitre XSLT. 9. PlainText Ce type permet de retourner du texte pur, c est à dire le contenu textuel d une page ou d un fichier source. Nous pouvons reprendre notre projet exemple04 et retourner après l authentification le code source de la page JSP. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="struts

121 default"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success" type="plaintext">/jsp/afficherclient.jsp</result> </action> </package> </struts> Affichage en mode plaintext exemple04-9 -

122 Gestion des exceptions La gestion des exceptions dans un projet est une étape essentielle. Les tests permettent bien entendu de mettre en évidence les bogues des programmes mais la gestion des exceptions apporte une sécurité de traitement au code. Chaque déclenchement d exception va être associé à une erreur 500 HTTP de la part du serveur. Pour gérer les exceptions, Struts propose la balise <exception-mapping/> qui permet d attraper les exceptions non traitées dans les actions. Cette balise contient deux attributs qui sont exception, pour spécifier le type d exception à attraper et result qui permet de préciser le résultat à déclencher en cas de levée d exception. Cette action résultat peut être précisée en local dans l action ou de manière globale avec la balise <globals-results/>. Si nous reprenons notre projet exemple04, nous pouvons ajouter volontairement le traitement ValiderAjouter.action d une conversion de chaîne de caractères en entier pour déclencher une exception. Code : exemple04.client.java package exemple04; import public class Client extends ActionSupport private String identifiant; private String motdepasse; public Client()... // ajouter les informations du client dans la session public String ajouter() // forcer le déclenchement d une exception int exception=integer.parseint(this.motdepasse); // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "success"; Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple04" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client"> - 1 -

123 <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <exception-mapping result="error" exception="java.lang.exception"/> <result name="error">/jsp/erreur.jsp</result> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> Gestion des exceptions Code : /jsp/erreur.jsp <html> <head> <title>erreur</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>erreur dans l application<br/> Veuillez contacter le webmestre</h3> </div> </body> </html> La balise <global-exception-mappings/> permet de gérer des groupes d exceptions. Par contre, une exception déclarée dans une balise <global-exception-mappings/> doit référencer un résultat déclaré dans une balise <globalresults/>. Nous pouvons adapter notre exemple précédent avec ce type de déclaration d exception. La balise Struts <s:property/> permet d afficher des informations sur les exceptions dans les vues JSP. Cette balise utilise l attribut exception.message pour utiliser le message associé aux exceptions dans le fichier de propriétés et l attribut exceptionstack pour afficher la trace de la pile d exception en détail. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> - 2 -

124 <package name="exemple04" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> <global-results> <result name="error">/jsp/erreur.jsp</result> </global-results> <global-exception-mappings> <exception-mapping result="error" exception="java.lang.exception"/> </global-exception-mappings> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple04.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> Code : /jsp/erreur.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>erreur</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>erreur dans l application<br/> Veuillez contacter le webmestre</h3> <s:property value="exceptionstack"/> <s:property value="exception.message"/> </div> </body> </html> Attributs exceptionstack et exception.message - 3 -

125 En résumé Ce chapitre a présenté la gestion des actions Struts. Dans un premier temps, les classes d action et leurs résultats associés ont été détaillés. La seconde partie du chapitre a proposé les différentes techniques d accès aux ressources et le traitement des paramètres. Ensuite, les paragraphes suivants ont été consacrés à la gestion du mapping et l invocation dynamique de méthodes. L avant dernier paragraphe présente sous la forme d exemples les différents types de résultats avec Struts et la dernière partie est consacrée aux traitements des exceptions

126 Présentation Le framework Struts 2 est livré en standard avec une librairie de tags ou balises, pour les pages JSP appelées vues. La bibliothèque de balises Struts est composée d une première catégorie pour la gestion des données et d une seconde pour les propriétés, paramètres et structures de contrôles (conditionnelles, boucles ). Ces balises permettent de programmer des services dynamiques côté client sans utiliser de code Java. Nous pourrons ainsi remplir des champs de formulaires, conserver des saisies ou afficher des objets avec facilité

127 Les tags de formulaire Ces balises XHTML sont utilisées dans les pages JSP pour afficher des données. La bibliothèque de tags Struts est livrée en standard avec l archive struts2 core x.jar. Pour utiliser cette librairie dans nos pages JSP, nous devons ajouter la déclaration suivante en haut de chaque page et préfixer nos balises par <s:nombalise/> : <%@ taglib prefix="s" uri="/struts-tags" %> Chaque balise Struts peut afficher du contenu statique ou du contenu dynamique à l aide d une expression OGNL (Object Graph Navigation Language). Les balises Struts sont déclarées dans le paquetage org.apache.struts2.components et reposent sur des déclarations JSTL courantes. Suivant le type d attribut utilisé (affichage, lecture, mise en forme ou autre), il existe plusieurs paramètres, comme la classe CSS à utiliser pour la mise en forme, le nom, le titre ou les actions JavaScript associées. 1. La balise <s:form/> Cette balise permet d afficher un formulaire HTML dans le document. Les paramètres habituels sont disponibles comme l action à exécuter, la méthode HTTP ou encore le type d encodage utilisé. Le projet exemple05 basé sur l application précédente exemple04 utilise la balise <s:form/> dans la page /jsp/ajouterclient.jsp. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" label="identifiant" /> <s:textfield name="motdepasse" label="mot de passe"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> Chaque balise Struts possède énormément de paramètres très utiles pour la mise en forme, les styles CSS à appliquer ou les actions JavaScript associées. La balise <s:form/> est automatiquement traduite par le moteur et transformée en tableau. La mise en forme est automatiquement réalisée par Struts. L alignement et le style sont gérés par les attributs de chaque balise. Nous pouvons afficher le code source de cette page afin d observer le code XHTML généré par Java. Code source généré : /jsp/ajouterclient.jsp <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <form id="validerajouter_client" name="validerajouter_client" action="/exemple05/validerajouter_client.action" method="post"> <table class="wwformtable"> <tr> <td class="tdlabel"><label for="validerajouter_client_identifiant" class="label">identifiant:</label></td> <td><input type="text" name="identifiant" value="" id="validerajouter_client_identifiant"/></td> </tr> - 1 -

128 <tr> <td class="tdlabel"><label for="validerajouter_client_motdepasse" class="label">mot de passe:</label></td> <td><input type="text" name="motdepasse" value="" id="validerajouter_client_motdepasse"/></td> </tr> <tr> <td colspan="2"><div align="right"><input type="submit" id="validerajouter_client_0" value="ajouter le client"/> </div> </td> </tr> </table> </form> </div> </body> </html> 2. Les thèmes de présentation et l attribut theme Comme nous l avons précisé ci dessus, Struts utilise par défaut un thème de présentation permettant de générer un affichage sous forme de tableau XHTML. Pour résumer, le tag <s:form/> est transformé en <form ><table /></form>. Cette mise en forme très pratique permet des développements rapides et adaptés aux parties administration des sites web mais pas forcément aux parties publiques. En effet, la tendance est à l utilisation de présentations optimisées avec des balises <span/> et <div/> ainsi que la mise en place massive de feuilles de style. Avec Struts les mises en formes appelées aussi templates sont gérées avec FreeMarker que nous verrons dans le chapitre consacré à cet outil. Les thèmes proposés par FreeMarker sont les suivants : xhtml : ce thème est celui proposé par défaut. Ce modèle permet d afficher les formulaires sous la forme de tableaux XHTML. simple : ce thème permet de traduire chaque balise Struts sous la forme la plus simple, c est à dire sans mise en forme pour l affichage. Avec ce thème, chaque balise sera traduite sans modification. css_xhmtl : ce thème permet de traduire chaque balise avec un modèle adapté aux feuilles de style CSS, c est à dire avec des balises <span/> et <div/>. ajax : ce thème permet de fournir un modèle pour l utilisation d Ajax. Un thème peut être utilisé au niveau le plus haut de la hiérarchie, dans la balise <s:form/> ou au niveau local dans chaque balise XHTML (ex : <s:textfield/>). Par défaut le thème utilisé est xhtml, il n est donc pas nécessaire de le préciser. Si nous voulons changer le type, nous pouvons utiliser l attribut themede chaque balise de présentation. Nous allons reprendre notre exemple05 et la page JSP AjouterClient.jsp afin de changer le thème. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client" theme="simple"> <s:textfield name="identifiant" label="identifiant" /> <s:textfield name="motdepasse" label="mot de passe"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> - 2 -

129 </html> Formulaire d ajout d un client avec le thème simple Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client" theme="css_xhtml"> <s:textfield name="identifiant" label="identifiant" /> <s:textfield name="motdepasse" label="mot de passe"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> Formulaire d ajout d un client avec le thème css_xhtml Code source généré: /jsp/ajouterclient.jsp <html> <head> <title>ajouter un client</title> - 3 -

130 <style url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <form id="validerajouter_client" name="validerajouter_client" action="/exemple05/validerajouter_client.action" method="post"> <div id="wwgrp_validerajouter_client_identifiant" class="wwgrp"> <div id="wwlbl_validerajouter_client_identifiant" class="wwlbl"> <label for="validerajouter_client_identifiant" class="label"> Identifiant: </label></div> <br /><div id="wwctrl_validerajouter_client_identifiant" class="wwctrl"> <input type="text" name="identifiant" value="" id="validerajouter_client_identifiant"/></div> </div> <div id="wwgrp_validerajouter_client_motdepasse" class="wwgrp"> <div id="wwlbl_validerajouter_client_motdepasse" class="wwlbl"> <label for="validerajouter_client_motdepasse" class="label"> Mot de passe: </label></div> <br /><div id="wwctrl_validerajouter_client_motdepasse" class="wwctrl"> <input type="text" name="motdepasse" value="" id="validerajouter_client_motdepasse"/></div> </div> <div align="right" id="wwctrl_validerajouter_client_0"><input type="submit" id="validerajouter_client_0" value="ajouter le client"/> </div> </form> </div> </body> </html> Nous pouvons bien entendu définir nos propres thèmes avec FreeMarker. Nous pouvons également utiliser un thème pour toute la page en utilisant un attribut nommé theme avec la portée page, request, session ou application. Il est également possible d assigner un thème à tout notre projet avec la propriété struts.ui.theme présente dans le fichier de propriétés struts.properties. 3. La balise <s:textfield/> La balise <s:textfield/> permet de créer un champ texte dans la page JSP. Nous retrouvons les attributs habituels HTML avec maxlength, readonly et size. 4. La balise <s:password/> La balise <s:password/> permet de créer un champ de type mot de passe dans la page JSP. Nous retrouvons les attributs habituels HTML avec maxlength, readonly et size. 5. La balise <s:submit/> La balise <s:submit/> permet de créer un bouton de validation du formulaire. Si nous reprenons notre page JSP AjouterClient.jsp, nous pouvons observer l utilisation des balises <s:textfield/>, <s:password/> et <s:submit/>. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> - 4 -

131 <s:textfield name="identifiant" label="identifiant" labelposition="top" cssclass="input" csserrorclass="inputerreur" tooltip="saisissez votre identifiant" tooltipconfig="# tooltipdelay : 500, tooltipicon : /images/aide. jpg "/> <s:password name="motdepasse" label="mot de passe" labelposition="top"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> Utilisation de la balise <s:submit/> 6. La balise <s:reset/> La balise <s:reset/> permet de vider les informations présentes dans le formulaire. Cette balise reprend les propriétés des balises précédentes. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:password name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter le client"/> <s:reset/> </s:form> </div> </body> - 5 -

132 </html> 7. La balise <s:label/> La balise <s:label/> permet de créer une balise HTML label afin d assigner un lien HTML permettant de positionner le curseur dans le champ concerné. 8. La balise <s:head/> La balise <s:head/> permet de créer une balise HTML d en tête pour le contenu des fichiers liés, comme les fichiers JavaScript ou Ajax. Cette balise n est pas très souvent utilisée. 9. La balise <s:textarea/> La balise <s:textarea/> permet de créer une balise HTML de type zone de texte multilignes. Cette balise contient notamment les attributs importants suivants cols et rows pour préciser respectivement le nombre de colonnes et lignes. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textarea name="motdepasse" label="mot de passe" labelposition="top" cols="18" rows="5"/> <s:submit value="ajouter le client"/> <s:reset/> </s:form> </div> </body> </html> - 6 -

133 Utilisation de la balise <s:textarea/> 10. La balise <s:checkbox/> La balise <s:checkbox/> permet de créer une case à cocher HTML. Ce tag Struts permet de gérer dynamiquement l état de la case (cochée ou pas). La réponse envoyée à la classe d action est false si la case n est pas cochée, et true si la case est cochée. Chaque balise checkbox est accompagnée d une balise HTML hidden. Si la balise n est pas cochée, seul le tag est présent sans le paramètre checkbox. Chaque attribut doit contenir ses accesseurs dans la classe d action pour fonctionner. Code : exemple05.client.java package exemple05; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private boolean newsletter; public Client() // getter et setter public boolean isnewsletter() return newsletter; public void setnewsletter(boolean newsletter) - 7 -

134 this.newsletter = newsletter; // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:checkbox name="newsletter" label="inscription à la newsletter"/> <s:submit value="ajouter le client"/> <s:reset/> </s:form> </div> </body> </html> Il est également possible d utiliser des collections Java pour gérer des listes de cases à cocher. Nous pourrons ainsi créer une liste avec des objets qui seront alors affichés dans chaque case à cocher avec le libellé et l état associé. 11. La balise <s:select/> La balise <s:select/> permet de créer une liste déroulante HTML. Ce type de composant est assez difficile à gérer en programmation classique lors du positionnement du bon item dans la liste. Pour gérer les valeurs transmises l attribut utilisé par Struts est nommé list. Cet attribut sera également utilisé pour les boutons radio qui représentent également des options. Le type de cet attribut list peut être un String, une énumération java.util.enumeration, un itérateur java.util.iterator, une collection java.util.map ou HashMap, ArrayList... Nous pouvons utiliser notre exemple précédent avec une collection. Code : exemple05.client.java package exemple05; import java.util.hashmap; import java.util.map; import - 8 -

135 public class Client extends ActionSupport private String identifiant; private String motdepasse; private boolean newsletter; private Map<Integer,String> listeprofessions=new HashMap<Integer,String>(); private int profession; public Client() public int getprofession() return profession; public void setprofession(int profession) this.profession = profession; public Map<Integer, String> getlisteprofessions() this.listeprofessions.put(1, "Informaticien"); this.listeprofessions.put(2, "Formateur"); return listeprofessions; public void setlisteprofessions(map<integer, String> listeprofessions) this.listeprofessions = listeprofessions; // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple05" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> - 9 -

136 <action name="ajouter_client" class="exemple05.client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple05.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success" type="chain">afficher_client</result> </action> </package> </struts> Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:checkbox name="newsletter" label="inscription à la newsletter"/> <s:select name="profession" label="profession" list="listeprofessions"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> La balise <s:select/> contient l attribut label qui correspond au texte qui sera affiché dans la page HTML. L attribut list permet de faire le lien avec les informations à afficher dans la liste. Enfin, l attribut name permet de nommer la liste déroulante. L attribut list est associé à des accesseurs présents dans la classe Client permettant de faire automatiquement le lien avec les valeurs. L attribut name est également associé à des accesseurs de type entier présents dans la classe Client afin de faire le lien avec le choix du client. Ainsi, lors des saisies ou erreurs de formulaires, l affichage sera automatiquement positionné sur le bon item de la liste déroulante. Nous allons utiliser un second exemple avec la classe Profession associée au client et une liste déroulante nommée profession associée à une collection listeprofessions de type List. L application est accessible à cette adresse : Code : exemple05.client.java package exemple05; import java.util.arraylist; import java.util.list; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private int profession; private List<Profession> listeprofessions=new ArrayList<Profession>(); public Client()

137 public List<Profession> getlisteprofessions() listeprofessions.add(new Profession(1, "Informaticien")); listeprofessions.add(new Profession(2, "Formateur")); listeprofessions.add(new Profession(3, "SGBDM")); listeprofessions.add(new Profession(4, "Responsable reseau")); return listeprofessions; public void setlisteprofessions(list<profession> listeprofessions) this.listeprofessions = listeprofessions; public int getprofession() return profession; public void setprofession(int profession) this.profession = profession; // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; // Classe de gestion des professions class Profession private int idprofession; private String nom; public Profession(int idprofession, String nom) this.idprofession=idprofession; this.nom=nom; public int getidprofession() return idprofession; public void setidprofession(int idprofession) this.idprofession=idprofession; public String getnom() return nom; public void setnom(string nom) this.nom=nom;

138 Classe : /vues/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:select name="profession" label="profession" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> Utilisation de la balise <s:select/> 12. La balise <s:optgroup/> La balise <s:optgroup/> permet, en association avec la balise <s:select/>, de créer une liste déroulante avec des groupes de données au format HTML. Ce type de composant, tout comme les listes simples, est assez difficile à gérer en programmation classique, afin de bien repositionner la liste sur le bon item lors d un choix. Nous allons reprendre notre exemple précédent en ajoutant une balise <s:optgroup/> pour gérer le niveau de qualification associé à la profession

139 Code : exemple05.client.java package exemple05; import java.util.arraylist; import java.util.hashmap; import java.util.list; import java.util.map; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private int profession; private List<Profession> listeprofessions=new ArrayList<Profession>(); private Map<Integer,String> niveauprofession=new HashMap<Integer,String>(); public Client() // getter et setter public Map<Integer, String> getniveauprofession() niveauprofession.put(1, "BAC"); niveauprofession.put(2, "BAC1"); niveauprofession.put(3, "BAC2"); return niveauprofession; // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; // Classe de gestion des professions class Profession private int idprofession; private String nom; public Profession(int idprofession, String nom) this.idprofession=idprofession; this.nom=nom; // getter et setter

140 Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:select name="profession" label="profession" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"> <s:optgroup label="niveau" list="niveauprofession"/> </s:select> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> Utilisation de la balise <s:optgroup/> La gestion de la balise <s:optgroup/> ne requiert pas l utilisation du setter associé

141 13. La balise <s:radio/> La balise <s:radio/> permet d utiliser les boutons radio HTML. Les boutons radio avec Struts reprennent le principe d utilisation des listes. Chaque bouton radio est représenté dans une liste et le choix unique est alors géré par le framework afin d alléger le travail du développeur. Le principe d utilisation étant identique aux listes, il est nécessaire d utiliser les accesseurs pour la liste elle même et pour le choix du bouton. Nous allons ajouter une liste de boutons radio pour le type de contrat du client. Code : exemple05.client.java package exemple05; import java.util.arraylist; import java.util.hashmap; import java.util.list; import java.util.map; import java.util.sortedmap; import java.util.treemap; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private int profession; private List<Profession> listeprofessions=new ArrayList<Profession>(); private Map<Integer,String> niveauprofession=new HashMap<Integer,String>(); private SortedMap<Integer,String> listecontrats=new TreeMap<Integer,String>(); private int contrat; public Client() // getter et setter public Map<Integer, String> getniveauprofession() niveauprofession.put(1, "BAC"); niveauprofession.put(2, "BAC1"); niveauprofession.put(3, "BAC2"); return niveauprofession; public SortedMap<Integer, String> getlistecontrats() listecontrats.put(1, "Temps partiel"); listecontrats.put(2, "Interim"); listecontrats.put(3, "Temps plein"); return listecontrats; public void setlistecontrats(sortedmap<integer, String> listecontrats) this.listecontrats = listecontrats; public int getcontrat() return contrat; public void setcontrat(int contrat) this.contrat = contrat; // ajouter les informations du client dans la session

142 public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; // Classe de gestion des professions class Profession private int idprofession; private String nom; public Profession(int idprofession, String nom) this.idprofession=idprofession; this.nom=nom; // getter et setter Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:select name="profession" label="profession" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"> <s:optgroup label="niveau" list="niveauprofession"/> </s:select> <s:radio name="contrat" label="type de contrat" list="listecontrats"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html>

143 Utilisation de la balise <s:radio/> 14. La balise <s:checkboxlist/> La balise <s:checkboxlist/> permet de créer des cases à cocher HTML. Ces composants utilisent des tableaux de chaînes de caractères ou une collection de types primitifs. Lorsqu une case à cocher est validée, la propriété associée dans la classe d action est initialisée, il est donc nécessaire d implémenter un tableau d entiers pour gérer les choix de l utilisateur. Nous allons continuer notre exemple avec des cases à cocher pour la gestion des repas du client. Code : exemple05.client.java package exemple05; import java.util.arraylist; import java.util.list; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private int profession; private int[] repas; private List<Profession> listeprofessions=new ArrayList<Profession>(); private List<Repas> listerepas=new ArrayList<Repas>(); public Client() // getter et setter public List<Repas> getlisterepas() listerepas.add(new Repas(1, "Repas du midi")); listerepas.add(new Repas(2, "Repas du soir")); return listerepas;

144 public void setlisterepas(list<repas> listerepas) this.listerepas = listerepas; public int[] getrepas() return repas; public void setrepas(int repas[]) this.repas = repas; // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; // Classe de gestion des professions class Profession private int idprofession; private String nom; public Profession(int idprofession, String nom) this.idprofession=idprofession; this.nom=nom; // getter et setter //Classe de gestion des repas class Repas private int id; private String nom; public Repas(int id, String nom) this.id=id; this.nom=nom; // getter et setter Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body>

145 <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:select name="profession" label="profession" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"/> <s:checkboxlist name="repas" label="repas" list="listerepas" listkey="id" listvalue="nom" labelposition="top"/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> Utilisation de la balise <s:checkboxlist/> 15. Les autres balises de formulaire Struts Struts propose d autres balises pour faciliter la mise en place de composants HTML. Nous retrouvons la balise <s:combobox/> qui propose une balise <input/> associée à une liste déroulante <select/>, cette balise permet d envoyer le texte et la valeur de la balise <option/> de la liste déroulante. La balise <s:updownselect/> permet de créer des listes multiples avec la possibilité de trier les items. La balise <s:optiontransferselect/> permet de créer un objet complexe avec une liste principale et une liste de destination afin de déplacer les items de la première liste vers la seconde. Enfin, la balise <s:doubleselect/> permet de créer deux listes déroulantes HTML associées. Les choix de la première liste entraînent des modifications dans la seconde. Toutes ces balises, bien que parfois utiles, ne sont pas très utilisées en développement Internet. Par expérience, il est préférable de développer ces différents outils en DHTML avec des librairies JavaScript ou Ajax (comme JQuery ou Prototype)

146 Les tags de contrôle Les balises Struts étudiées précédemment permettent de gérer la présentation et l affichage des données dans les vues. Cependant, le framework fournit également un ensemble de balises pour la gestion des accès aux données, des conditionnelles ou boucles de programmation ou encore la manipulation des propriétés de l application. 1. La balise <s:property/> La balise <s:property/> permet d afficher, à partir d expressions OGNL, une information présente dans la classe d action associée à ses accesseurs ou dans le contexte de l application (application, session, request, parameters, attr). L attribut value permet de donner le nom de la propriété et le paramètre espace permet d échapper les caractères HTML spéciaux (,, &, < et >). Nous allons reprendre notre exemple précédent exemple05 et utiliser les balises de contrôle dans notre nouveau projet exemple06. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:property value=" webmestre"/> <s:submit value="ajouter le client"/> </s:form> </div> </body></html> Utilisation de la balise <s:property/> - 1 -

147 La notation JSP EL Expression Language peut également être utilisée avec le dollar et les accolades : $ webmestre. Le second exemple permet de lire une valeur présente dans le contexte de l application. La portée de la variable est précisée dans le nom de la variable avec le caractère dièse #. Nous ajoutons un paramètre d application dans le fichier /WEB INF/web.xml pour l adresse e mail de contact. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="utf-8"?> <web-app id="webapp_9" version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <context-param> <param-name> contat</param-name> <param-value>contact@gdawj.com</param-value> </context-param> <filter> <filter-name>struts2</filter-name> <filterclass>org.apache.struts2.dispatcher.ng.filter.strutsprepareandexecutef ilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> webmestre : <s:property value=" webmestre"/><br/> webmestre : $ webmestre<br/> contact : <s:property value="#application. contat"/><br/> <s:submit value="ajouter le client"/> </s:form> </div> </body> </html> - 2 -

148 Utilisation de la notation EL 2. La balise <s:a/> La balise <s:a/> permet de créer un lien HTML conforme. Cette balise est très peu utilisée avec Struts car elle n apporte pas véritablement d avantage par rapport au tag HTML standard si ce n est le fait de gérer l id de session. 3. La balise <s:action/> La balise <s:action/> est utilisée pour déclencher une action et récupérer le résultat de celle ci dans une variable. Par exemple, nous pouvons déclencher une action et stocker le résultat dans une variable nommée objet. <s:action var="objet" name="monactionadeclencher"/> 4. La balise <s:param/> La balise <s:param/> permet de passer des paramètres à une action. Elle est utilisée dans des liens ou des formulaires. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> - 3 -

149 webmestre : <s:property value=" webmestre"/><br/> webmestre : $ webmestre<br/> contact : <s:property value="#application. contat"/><br/> <s:submit value="ajouter le client"/> </s:form> <s:url id="afficherurl" action="afficher" > <s:param name="pagecourante">$header.host</s:param> </s:url> <s:a href="%afficherurl">passer un paramètre à une action</s:a> </div> </body> </html> Une URL est définie ainsi qu un paramètre nommé pagecourante permettant de récupérer le nom de l hôte local. Le lien Struts est alors automatiquement mis en forme avec ces informations. 5. La balise <s:bean/> La balise <s:bean/> permet de créer une instance d une classe JavaBean. Ce tag est similaire à la balise JSP <jsp:usebean/>. Cette balise fournit l attribut name permettant de définir une classe JavaBean et l attribut id qui permet de donner un nom à l instance. Cette balise <s:bean/> est très souvent utilisée en association avec la balise <s:param/> pour assigner les valeurs au JavaBean. Nous allons définir une nouvelle classe JavaBean nommée Profession et utiliser la balise <s:bean/>. Code : exemple06.profession.java package exemple06; public class Profession private int id; private String nom; public Profession() // getter et setter Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:bean name="exemple06.profession" id="profession"> <s:param name="id" value="1"/> <s:param name="nom" value=" Formateur "/> </s:bean> Profession : <s:property value="#profession.id"/> <s:property value="#profession.nom"/> </div> </body> </html> - 4 -

150 L initialisation d une propriété de type chaîne de caractères est réalisée avec des quotes sous Struts. Nous remarquons dans l exemple l initialisation de la propriété nom pour la profession. <s:param name="nom" value=" Formateur "/>. 6. La balise <s:date/> La balise <s:date/> est utilisée pour formater l affichage des dates. Cette balise est très utile pour afficher des dates en fonction du pays de l utilisateur courant. Par exemple, un utilisateur français préfèrera une date au format jj/mm/aaaa alors qu un anglais préfèrera l affichage sous la forme aaaa mm jj. La modification de la vue JSP permet de gérer deux objets date avec les deux types d affichages précédents. Code : /jsp/dateclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <s:bean name="java.util.date" id="date"/> Date : <s:property value="#date"/><br/> Date FR : <s:date name="#date" var="format_fr" format="dd/mm/yyyy"/><s:property value="format_fr"/><br/> Date EN : <s:date name="#date" var="format_en" format="yyyy-mm-dd"/><s:property value="format_en"/><br/> </div> </body> </html> 7. La balise <s:set/> La balise <s:set/> permet de créer une propriété associée à sa valeur dynamique (type primitif ou objet). La variable peut être créée dans le contexte de l application, dans la session, dans la requête courante ou dans la page. Code : /jsp/dateclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <s:bean name="java.util.date" id="date"/> <s:set name="daterequete" value="#date"/> Date avec une nouvelle variable : <s:property value="#daterequete"/> </div> </body> </html> - 5 -

151 Utilisation de la balise <s:set/> Cette balise est très utilisée pour stocker un objet complexe dans une variable afin d accéder à ses valeurs dans la suite du code. 8. La balise <s:push/> La balise <s:push/> est très proche de la balise <s:set/>. Elle permet d initialiser un objet mais de l utiliser entre sa balise de début <s:push> et sa balise de fin </s:push>. 9. La balise <s:url/> La balise <s:url/> permet de créer des URLs dynamiques qui seront par la suite utilisées dans des liens ou des formulaires HTML. Cette balise a été utilisée dans l exemple précédent pour la mise en forme d un lien HTML à l aide de la balise <s:param/>. <s:url id="afficherurl" action="afficher" > <s:param name="pagecourante">$header.host</s:param> </s:url> <s:a href="%afficherurl">passer un paramètre à une action</s:a> 10. Les balises <s:if/>, <s:else/> et <s:elseif/> Les balises <s:if/>, <s:else/> et <s:elseif/> sont utilisées pour réaliser des tests conditionnels. Ces balises permettent de tester si un attribut est nul. <s:if test="#profession.id==null"> Pas de profession </s:if> <s:else> Profession <s:property value="#profession.id"/> </s:else> 11. La balise <s:iterator/> La balise <s:iterator/> permet de parcourir un tableau ou une collection Java. Nous allons reprendre l exemple avec les professions pour afficher les valeurs. Code : exemple06.profession.java package exemple06; - 6 -

152 public class Profession private int id; private String nom; public Profession() public Profession(int id, String nom) this.id=id; this.nom=nom; // getter et setter Code : exemple06.client.java package exemple06; import java.util.arraylist; import java.util.list; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private String webmestre="jlafosse@gdawj.com"; private int profession; private List<Profession> listeprofessions=new ArrayList<Profession>(); public Client() public List<Profession> getlisteprofessions() listeprofessions.add(new Profession(1, "Informaticien")); listeprofessions.add(new Profession(2, "Formateur")); listeprofessions.add(new Profession(3, "SGBDM")); listeprofessions.add(new Profession(4, "Responsable reseau")); return listeprofessions; public void setlisteprofessions(list<profession> listeprofessions) this.listeprofessions = listeprofessions; public int getprofession() return profession; public void setprofession(int profession) this.profession = profession; // getter et setter - 7 -

153 // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter le client"/> </s:form> <s:iterator value="listeprofessions"> <s:property value="id"/> - <s:property value="nom"/><br/> </s:iterator> </div> </body> </html> - 8 -

154 Utilisation de la balise <s:iterator/> La balise <s:iterator/> permet de réaliser des boucles sur des valeurs afin de simuler des parcours de valeurs. Ce second exemple de la vue JSP permet d afficher une liste à puces numérotées. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter le client"/> </s:form> <s:iterator value="listeprofessions"> <s:property value="id"/> - <s:property value="nom"/><br/> </s:iterator> <ul> <s:iterator value="1,2,3,4,5,6,7,8,9" status="status"> <li><s:property/> - <s:property value="#status.index"/> - <s:property value="#status.count"/> - <s:property value="#status.even"/> - <s:property value="#status.odd"/> - <s:property value="#status.last"/> </li> </s:iterator> </ul> </div> </body> </html> - 9 -

155 Utilisation de la balise <s:iterator/> 12. La balise <s:append/> La balise <s:append/> permet de concaténer des itérateurs pour des listes de données. Cette balise est utilisée principalement pour créer une nouvelle liste à partir de deux autres. Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter le client"/> </s:form> <s:iterator value="listeprofessions">

156 <s:property value="id"/> - <s:property value="nom"/><br/> </s:iterator> <br/> <s:set var="liste1" value=" informaticien, formateur "/> <s:set var="liste2" value=" administrateur reseau, programmeur "/> <s:append var="listetotale"> <s:param value="liste1"/> <s:param value="liste2"/> </s:append> <s:iterator value="#listetotale"> <s:property/><br/> </s:iterator> </div> </body> </html> Utilisation de la balise <s:append/> 13. La balise <s:sort/> La balise <s:sort/> est utilisée pour trier les éléments d un itérateur d une collection. Code : exemple06.client.java package exemple06; import java.util.arraylist; import java.util.comparator; import java.util.list;

157 import public class Client extends ActionSupport private String identifiant; private String motdepasse; private String private int profession; private List<Profession> listeprofessions=new ArrayList<Profession>(); public Client() // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return "afficher"; // comparer deux objets public Comparator<Object> getmycomparator() return new Comparator<Object>() public int compare(object o1, Object o2) return o1.tostring().compareto(o2.tostring()); ; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter le client"/> </s:form> <s:sort comparator="mycomparator" source="listeprofessions"

158 id="trilisteprofessions"/> <s:iterator value="#attr.trilisteprofessions"> <s:property value="id"/> - <s:property value="nom"/><br/> </s:iterator> </div> </body> </html>

159 En résumé Ce chapitre a présenté les différentes balises ou tags Struts pour la mise en forme des données et les actions. La première partie détaille les balises de formulaires utilisées dans les pages JSP ou vues, afin de faciliter la mise en forme des données. Le second paragraphe présente, toujours au travers d exemples, les balises de contrôles pour l accès aux informations, les structures et les tris

160 Présentation La gestion des messages d erreurs et de succès est une tâche très importante lors des développements d applications informatiques. L internationalisation en programmation et développement consiste à gérer les langues et donc à proposer des pages multilingues. Le terme i18n est standardisé pour l internationalisation et correspond en fait, aux 18 caractères qui composent le mot internationalisation entre le i et le n. En programmation, la langue est présentée avec une locale. Cette locale est définie en fonction de la langue et du pays. Une locale est composée de deux paramètres : La première partie qui est lang, le caractère underscore et la seconde partie qui est country. Nous aurions donc par exemple le français en France représenté par : fr_fr. Le français canadien représenté par : fr_ca. L anglais américain représenté par : en_us. Pour la mise en œuvre de la localisation des messages en Java, nous utilisons un ensemble de fichiers appelés bundle en anglais. Un fichier de propriétés est utilisé par langue/locale. Chaque fichier possède un préfixe commun appelé basename et doit avoir l extension.properties. Les fichiers pour des langues particulières doivent avoir le même préfixe suivi d un caractère underscore et du code langue. Pour être accessibles, ces fichiers doivent être inclus dans le classpath. L internationalisation permet de changer l affichage du texte ou des images par exemple, en fonction du type d utilisateur visiteur. La plate forme Java EE a été conçue pour supporter les applications multilingues avec un principe simple et efficace qui est en partie utilisé par Struts. Les classes d actions Struts utilisent la méthode gettext() pour lire les messages présents dans un fichier texte de propriétés en fonction de la langue. Enfin, chaque application professionnelle devrait utiliser le mécanisme d internationalisation même lors des développements monolingues (une seule langue). En effet, le recourt à un fichier de propriétés, même dans une seule langue, permet d améliorer la maintenance future et de modifier les textes statiques sans être obligé de recompiler l application

161 Mise en application Comme nous l avons indiqué précédemment, une locale est définie par une langue et un pays. Nous retrouvons par exemple l anglais américain (en_us) et l anglais au canada (en_ca). La langue est la partie la plus importante de la définition et peut dans la plupart des cas, être utilisée sans le pays (ex : proprietes_fr.properties, proprietes_en.properties). Une application multilingue utilise des fichiers textes composés de clé/valeur pour chaque locale. Chaque paire de valeurs est composée d une clé unique et de sa valeur associée. Chaque clé est de type chaîne de caractères et chaque valeur de clé peut être de n importe quel type. Chaque fichier de propriétés utilisé pour lire les informations doit être nommé sous la forme suivante : basename_langue_pays.properties Nous retrouvons ainsi le champ basenamecorrespondant au nom du fichier (ex : ressources), la langue (ex : fr), le pays (ex : FR) et enfin, l extension du fichier (.properties). Pour nos exemples, nous aurons ainsi deux fichiers : ressources_fr.properties et ressources_en.properties. L utilisation de l internationalisation avec Struts est simple, chaque classe héritant de la classe ActionSupport peut utiliser l internationalisation. De même, l interface com.opensymphony.xwork2.textprovider fournit un accès aux bundles du projet. La méthode gettext(string cle) permet de récupérer la valeur associée à la clé passée en paramètre. Cette méthode retourne null si la clé n est pas trouvée dans le fichier de propriétés. La seconde signature de la méthode gettext(string cle, String valeurdefaut) permet de récupérer la valeur associée à la clé passée en paramètre et la valeur par défaut si la clé n est pas trouvée dans le fichier de propriétés. La troisième signature gettext(string cle, String[] format) permet de récupérer la valeur associée à la clé passée en paramètre, en association avec le format spécifié en paramètre. La signature gettext(string cle, String valeurdefaut, String[] format) permet de récupérer la valeur associée à la clé passée en paramètre, en association avec la valeur par défaut et le format associé

162 Accès aux propriétés L accès aux données présentes dans les fichiers de propriétés par l intermédiaire des méthodes dédiées comme gettext ( ) est utilisé dans l ordre suivant : Dans un fichier de propriétés possédant le même nom que la classe d action et présent dans le répertoire /WEB INF/classes au même niveau que la classe d action. Ex. : la classe Client.java est associée avec le fichier de propriétés Client.properties. Cette technique bien que très simple à mettre en œuvre est très peu utilisée pour des raisons de souplesse lors de la maintenance (un fichier par classe). Dans un fichier de propriétés possédant le même nom qu une interface utilisée par chaque classe qui en a besoin. Ex. : l interface Ressources qui est implémentée par chaque classe utilisatrice et le fichier Ressources.properties. Dans un fichier de propriétés possédant le même nom qu une classe héritée par chaque classe qui en a besoin. Ex. : le fichier de propriétés ActionSupport.properties qui est utilisé par chaque classe qui hérite de la classe ActionSupport. Dans un fichier de modèle si nos classes implémentent l interface com.opensymphony.xwork2.modeldriven. Dans un fichier de propriétés présent dans le paquetage par défaut du projet. Ex. : paquetage exemple07, /exemple07/ressources.properties. Dans un fichier de propriétés présent dans un sous paquetage de l application. Ex. : exemple07.ressources. Pour afficher les données présentes dans le fichier de propriétés, nous pouvons utiliser la balise <s:property/> ou l attribut label des balises associés à la méthode gettext( ) : %gettext( cle ). Nous allons reprendre notre exemple précédent de gestion des comptes clients afin de manipuler les messages du fichier de propriétés. Le nouveau projet utilisé est disponible à cette adresse : Arborescence exemple07 Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> - 1 -

163 <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple07" namespace="/" extends="struts-default"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client" class="exemple07.client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple07.client" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> Code : exemple07.client.java package exemple07; import public class Client extends ActionSupport private String identifiant; private String motdepasse; public Client() // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return SUCCESS; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.ajouter )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3><s:property value="%gettext( client.ajouter )"/></h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" - 2 -

164 label="%gettext( client.identifiant )" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="% gettext( client.motdepasse )" labelposition="top" cssclass="input"/> <s:submit value="%gettext( client.ajouter )"/> </s:form> </div> </body> </html> Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4><s:property value="% gettext( client.afficher )"/></h4> <s:property value="%gettext( client.identifiant )"/>: <s:property value="identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/>: <s:property value="motdepasse"/><br/> </p> </div> </body> </html> Code : /exemple07/package.properties client.ajouter=ajouter un client client.identifiant=identifiant client.motdepasse=mot de passe client.afficher=afficher un client Ajouter un client exemple07 Nous pouvons remarquer que dans notre exemple, l accès aux données est réalisé dans le fichier /exemple07/package.properties présent dans le paquetage par défaut et qui sera donc accessible par toutes les classes du paquetage. Ce fichier est composé de couples clé/valeur au format texte. L accès à ces informations est réalisé de deux manières dans les vues, avec labalise <s:property/> et l attribut label : <s:property value="%gettext( client.ajouter )"/> - 3 -

165 <s:textfield name="identifiant" id="identifiant" label="% gettext( client.identifiant )"/> Cette application bien que monolingue permet de gérer facilement des messages en éditant simplement des fichiers de - 4 -

166 Données multilingues Nous avons montré dans l exemple précédent les deux techniques offertes par Struts pour accéder aux données des fichiers de propriétés que sont la balise <s:property/> et l attribut label des différentes balises. Struts propose également la balise <s:text/> pour l accès aux propriétés. Cette balise est équivalente à l utilisation de la méthode gettext( ) dans la balise <s:property/>, elle permet uniquement une écriture plus légère de l accès aux données. Les déclarations suivantes sont ainsi équivalentes : <s:property value="%gettext( client.ajouter )"/> <s:text name="client.ajouter"/> Si l attribut id est présent dans la balise <s:text/>, celle ci n est pas affichée mais stocke dans la variable précisée la propriété. Cette propriété peut alors être affichée avec la balise <s:property/>. <s:text name="client.ajouter" id="msg_clientajouter"/> <s:property value="#msg_clientajouter"/> Enfin, la balise <s:text/> permet de passer des paramètres présents dans le fichier de propriétés. Cette technique est très utile pour améliorer l ergonomie. Nous allons ajouter une ligne à notre fichier de propriétés paquetage.properties. client.bienvenue=bonjour 0 Nous pouvons alors passer l identifiant de la personne authentifiée par l intermédiaire de la balise <s:text/> dans la page /jsp/afficherclient.jsp. Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4><s:property value="% gettext( client.afficher )"/></h4> <s:property value="% gettext( client.identifiant )"/>: <s:property value="identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/ >: <s:property value="motdepasse"/><br/> <s:text name="client.bienvenue"> <s:param><s:property value="identifiant"/></s:param> </s:text> </p> </div> </body> </html> - 1 -

167 Affichage des informations client exemple07 Nous allons maintenant utiliser un second fichier de propriétés package_en.properties pour gérer les affichages en anglais. Pour cela, nous réalisons un simple copier/coller du fichier avec les traductions associées. Code : /exemple07/package_en.properties client.ajouter=add a customer client.identifiant=login client.motdepasse=password client.afficher=show a customer client.bienvenue=hello 0 Avant de tester ces fonctionnalités, nous allons mettre notre navigateur en anglais. Pour cela, avec Firefox nous utilisons le menu Outils Options Contenu Langues Choisir et la langue : Anglais. Pour la prise en compte de ces opérations, il est nécessaire de fermer le navigateur et d ouvrir une nouvelle fenêtre. Modification de la langue par défaut du navigateur - 2 -

168 Nous pouvons ouvrir à nouveau notre navigateur et lancer la page pour le formulaire client. Le site utilise alors automatiquement les traductions présentes dans le fichier associé à la locale _en (package_en.properties). Formulaire client anglais exemple07 Le principe est identique quel que soit le nombre de langues utilisées. Nous aurons autant de fichiers de propriétés suffixés par la locale adaptée que de langues utilisées dans l application. La balise <s:i18n/> permet de charger dynamiquement des ressources ou bundle. Cette technique est principalement utilisée pour associer une clé à un objet complexe dynamique qui n est pas une chaîne de caractères. La balise <s:i18n/> contient un attribut name permettant de spécifier le bundle à utiliser. Pour illustrer notre exemple, nous allons définir une classe nommée MesRessources.java retournant un contenu de type complexe adapté au type d authentification. Ainsi, si l utilisateur connecté est en français, la date de connexion sera automatiquement affichée au format jj/mm/aaaa et s il est en anglais, la date de connexion sera affichée au format aaaa mm dd. Les classes d utilisation des bundles dynamiques doivent hériter de la classe java.util.listresourcebundle. Code : exemple07.mesressources.java package exemple07; import java.text.simpledateformat; import java.util.date; import java.util.listresourcebundle; public class MesRessources extends ListResourceBundle // objet date et mise en forme Date date=new Date(); SimpleDateFormat datejour=new SimpleDateFormat("dd/MM/yyyy"); // retourner le contenu adapté public Object[][] getcontents() return traductions; // contenu dynamique (cle/valeur) Object[][] traductions= "client.bienvenue","bonjour 0 - connexion : "+datejour.format(date) ; - 3 -

169 Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4><s:property value="% gettext( client.afficher )"/></h4> <s:property value="% gettext( client.identifiant )"/>: <s:property value="identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/ >: <s:property value="motdepasse"/><br/> <s:i18n name="exemple07.mesressources"> <s:text name="client.bienvenue"> <s:param name="identifiant"><s:property value="identifiant"/></s:param> </s:text> </s:i18n> </p> </div> </body> </html> Affichage du client exemple07 Maintenant, si nous souhaitons utiliser un message d authentification adapté pour l anglais (locale en), nous devons créer une classe de même nom mais suffixée par la locale (MesRessources_en.java). Code : exemple07.mesressources_en.java package exemple07; import java.text.simpledateformat; import java.util.date; import java.util.listresourcebundle; public class MesRessources_en extends ListResourceBundle // objet date et mise en forme Date date=new Date(); SimpleDateFormat datejour=new SimpleDateFormat("yyyy-mm-dd"); - 4 -

170 // retourner le contenu adapté public Object[][] getcontents() return traductions; // contenu dynamique (cle/valeur) Object[][] traductions= "client.bienvenue","hello 0 - connection : "+datejour.format(date) ; Afficher client exemple07-5 -

171 Sélection dynamique des fichiers Les fichiers de propriétés utilisés jusqu à maintenant sont chargés en fonction de la locale du navigateur et donc du choix de la langue. Nous avons utilisé les propriétés du navigateur pour forcer le choix de la langue. Cette manipulation a d ailleurs nécessité le rechargement du navigateur. Dans une application professionnelle, les langues doivent pouvoir être changées dynamiquement par l intermédiaire de petits drapeaux représentant les langues par exemple. La classe ActionSupport propose pour cela, une solution d internationalisation. Pour pouvoir changer dynamiquement la langue de l application, Struts propose le paramètre request_locale. Nous allons utiliser ce paramètre pour changer la langue par l intermédiaire d un lien HTML. Ces liens vont être placés dans la première action et la vue associée AjouterClient.jsp. Bien entendu, nous pourrions utiliser n importe quelle page JSP ou JSPF pour gérer les langues (ex : entete.jspf). Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.ajouter )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <s:url action="ajouter_client" id="urllanguefr"> <s:param name="request_locale">fr</s:param> </s:url> <s:url action="ajouter_client" id="urllangueen"> <s:param name="request_locale">en</s:param> </s:url> <ul> <li><s:a href="% urllanguefr">français</s:a></li> <li><s:a href="%urllangueen">anglais</s:a></li> </ul> <br/> <h3><s:property value="%gettext( client.ajouter )"/></h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="%gettext( client.identifiant )" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" label="% gettext( client.motdepasse )" labelposition="top" cssclass="input"/> <s:submit value="%gettext( client.ajouter )"/> </s:form> </div> </body> </html> La première partie de code permet uniquement de créer des liens HTML avec les paramètres. Elle peut être remplacée par les lignes suivantes : <ul><li><a href="ajouter_client.action? request_locale=fr">français</a></li><li><a href="ajouter_client.action? request_locale=en">anglais</a></li></ul> - 1 -

172 Gestion des langues avec la balise <s:i18n/> - 2 -

173 Accès aux ressources dans les classes Parfois, lors des développements, nous avons besoin d accéder aux ressources des fichiers de propriétés dans les classes d une application pour gérer les messages d erreurs ou de succès. Comme nous l avons indiqué plus haut, chaque classe qui hérite de la classe ActionSupport peut utiliser les fichiers de propriétés. L accès aux couples de données clé/valeur est réalisé par l intermédiaire de la fonction gettext( ) étudiée précédemment. Nous allons modifier notre classe Client.java afin d ajouter un message dynamique suite à l authentification du client. Pour cela, nous utilisons une variable de classe nommée message ainsi que son getter associé pour accéder à sa valeur dans les vues JSP. Code : exemple07.client.java package exemple07; import public class Client extends ActionSupport private String identifiant; private String motdepasse; private String message; public Client() // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else // message dynamique présent dans le fichier de propriétés this.message=gettext("client.accueil"); return SUCCESS; Code : exemple07.package.properties client.accueil=merci de votre authentification Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4><s:property value="% - 1 -

174 gettext( client.afficher )"/></h4> <s:property value="% gettext( client.identifiant )"/>: <s:property value="identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/ >: <s:property value="motdepasse"/><br/> </p> </div> </body> </html> <s:property value="message"/> Affichage des informations client avec accès aux messages - 2 -

175 En résumé Ce chapitre a présenté la gestion des messages de propriétés et le mécanisme d internationalisation avec Struts. Chaque application professionnelle doit utiliser ce mécanisme évolué permettant de faciliter la maintenance et l évolution d un projet mono et multilingue

176 Présentation La validation des données permet d améliorer la sécurité d un système ainsi que la robustesse de celui ci au travers de règles et de contrôles. Les validations permettent également de conserver un système cohérent et homogène, chaque donnée ou objet inséré dans la base de données sont vérifiés avant insertion ou modification. Les vérifications et contrôles effectués sur les champs d un système sont contraignants à réaliser et représentent une étape complexe à mettre en œuvre dans une application Java EE. Struts fournit pour cela une mise en place de validation simple et puissante basée sur le framework de validation XWork. Les validateurs Struts ne requièrent pas de programmation. Chaque définition de règle est configurée en rapport à une propriété ou un objet dans un fichier texte au format XML facilement maintenable ou avec les annotations Java. De même, Struts propose en accord avec ses standards, l utilisation de règles dans les fichiers de validation, l association des messages aux fichiers de propriétés ou bundle et les validations en programmation dans les classes

177 Mise en place Avec Struts, deux types de validateurs sont utilisés : Les validateurs de type champ ou attribut. Ces validateurs sont utilisés en rapport avec des champs de formulaires HTML et sont associés à une ou plusieurs règles de validation. Les validateurs de type conditionnel qui ne sont pas associés à des champs de formulaires mais qui permettent de faire des tests en programmation dans les classes d actions des applications. La mise en place des validations sur une application Struts est décomposée en trois étapes : Étape 1 : définir quelle action doit vérifier ses entrées. Étape 2 : écrire le fichier de validation associé. Le nom du fichier repose sur le modèle suivant : NomClasseAction validation.xml. Étape 3 : définir la page qui devra être appelée suite à une erreur de saisie. Cette définition est réalisée dans le fichier de configuration struts.xml par l intermédiaire de la balise <result name="input"/>. Le nom du fichier de configuration dépend des validations que nous souhaitons réaliser. Le modèle présenté ci dessus avec le nom de la classe suffixée par le terme validation est le plus courant (NomClasseActionvalidation.xml). Cependant, si nous souhaitons réaliser une vérification uniquement sur la méthode ajoutermodifier_client() de la classe d action ClientAction, nous aurons alors un fichier de la forme ClientActionajoutermodifier_Client validation.xml. Cette technique permet ainsi d appliquer les validations sur une action particulière du contrôleur ou d utiliser un fichier de validation, pour par exemple, la création d un client et un autre pour la modification avec des règles différentes. Les fichiers de validation Struts doivent commencer par la définition de la grammaire adaptée. La balise racine du document XML est nommée <validators/>. Cette balise peut contenir des balises <field/> en rapport avec les champs de formulaires ou des balises <validator/> pour la définition des validateurs associés. La balise <field/> contient un attribut nommé name qui fait le lien avec le nom d un champ du formulaire HTML à valider. Nous pouvons appliquer autant de validations que l on souhaite par champs de formulaires. La balise <field/> contient elle même une ou plusieurs balises <field-validator/> associées à un attribut type définissant le type de validation à réaliser (champ obligatoire, e mail, entier ). Nous pouvons également passer des paramètres à une balise <field/> afin de préciser une valeur ou une plage. Enfin, la balise <message/> présente dans une balise <field-validator/> permet de préciser le message à afficher en cas d erreur de validation. Nous allons mettre en application les validations à partir de notre formulaire client. Le nouveau projet, nommé exemple08 est basé sur l application exemple07. Nous renommons notre classe d action Client en ClientAction afin de respecter les conventions Struts

178 Arborescence du projet exemple08 Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple08" namespace="/" extends="struts-default"> <default-action-ref name="ajouter_client" /> <action name="ajouter_client" class="exemple08.clientaction"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple08.clientaction" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> Code : exemple08.clientaction.java package exemple08; import public class ClientAction extends ActionSupport private String identifiant; private String motdepasse; // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return SUCCESS; Code : /exemple08/clientaction-validerajouter_client-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" " <validators> - 2 -

179 <field name="identifiant"> <field-validator type="requiredstring"> <message>le champ identifiant est obligatoire</message> </field-validator> </field> <field name="motdepasse"> <field-validator type="requiredstring"> <message>le champ mot de passe est obligatoire</message> </field-validator> </field> </validators> Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" id="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter un client"/> </s:form> </div> </body> </html> Les définitions mises en place sont simples et reposent sur les champs nommés identifiant et motdepasse du formulaire. La relation entre les champs du formulaire et le fichier de validation est réalisée par les balises <field name="identifiant"> et <field name="motdepasse">. Dans notre cas, nous vérifions uniquement si les champs ne sont pas vides. La définition du routage est réalisée dans le fichier struts.xml et permet de préciser qu en cas d erreur de validation, la page précisée par le paramètre input sera alors appelée sans perte des saisies. <action name="validerajouter_client" class="exemple08.clientaction" method="ajouter"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> Nous pouvons définir la classe CSS.errorMessage proposée par Struts afin de donner une couleur adaptée (rouge) pour l affichage des messages d erreurs. Le style suivant est ajouté dans le fichier /css/styles.css..errormessage color:#d53a3e; font-family:tahoma, verdana, arial, sans-serif; font-size:13px; Désormais, nous pouvons essayer l ajout d un compte client sans saisir d identifiant et de mot de passe

180 Formulaire d ajout client et validation des saisies Maintenant, nous pouvons améliorer l affichage en utilisant la balise proposée par Struts <s:fielderror/> afin d afficher les messages d erreurs dans les vues adaptées. Cette balise est ainsi ajoutée avec un style CSS adapté pour la mise en forme. Code : /css/styles.css #message_erreur margin-top:16px; margin-bottom:0px; margin-left:40px; padding-bottom:2px; padding-top:2px; padding-left:20px; padding-right:50px; text-align:justify; border-style:solid; border-top-width:1px; border-top-color:#d53a3e; border-right-width:2px; border-right-color:#d53a3e; border-bottom-width:2px; border-bottom-color:#d53a3e; border-left-width:1px; border-left-color:#d53a3e; width:600px; #message_erreur ul padding-top:0px; margin-top:2px; padding-bottom:0px; padding-right:20px; margin-bottom:0px; margin-left:8px; margin-right:10px; color:#d53a3e; font-family:tahoma, verdana, arial, sans-serif; font-size:13px; font-weight:normal; list-style-image:url(../images/important.gif); - 4 -

181 #message_erreur label color:#d53a3e; font-family:tahoma, verdana, arial, sans-serif; font-size:12px; font-weight:bold; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> </body> </html> Formulaire ajout client et feuille de styles CSS - 5 -

182 Validations Dans l exemple précédent, nous avons utilisé la validation de type requirestring qui correspond à une vérification de la présence d une chaîne de caractères non vide. Struts propose plusieurs validations en standard pour faciliter les validations : required, requiredstring, int, date, expression, fieldexpression, e mail, url, visitor, conversion, stringlenght ou regexp. Nous allons présenter chacune de ces validations afin de comprendre leur intérêt. 1. required Cette validation permet de vérifier qu un champ n a pas la valeur null. Cependant, une chaîne de caractères vide n est pas nulle, elle est présente mais vide ce qui est différent. 2. requiredstring Cette validation permet de tester si un champ n a pas la valeur null et non vide. Cette validation contient un paramètre nommé trim qui correspond aux espaces avant et après la saisie. <field-validator type="requiredstring"> <param name="trim">true</param> <message>le champ identifiant est obligatoire</message> </field-validator> 3. stringlength Cette validation permet de valider la taille d un champ non vide. Nous pouvons alors spécifier la taille minimale et maximale d un champ de formulaire. Dans notre exemple, le champ identifiant doit avoir au minimum 4 caractères et 10 au maximum. Code : /exemple08/clientaction-validerajouter_client-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" " <validators> <field name="identifiant"> <field-validator type="stringlength"> <param name="minlength">4</param> <param name="maxlength">10</param> <message>le champ identifiant doit avoir entre 4 et 10 caracteres</message> </field-validator> </field> <field name="motdepasse"> <field-validator type="requiredstring"> <message>le champ mot de passe est obligatoire</message> </field-validator> </field> </validators> - 1 -

183 Formulaire client et validation de la taille des champs 4. int Cette validation permet de vérifier si un champ peut être converti en entier et si les paramètres min et max sont utilisés, elle permet de vérifier que la saisie est bien comprise dans la plage de données indiquée. <field-validator type="int"> <param name="min">0</param> <param name="max">20</param> <message>le champ identifiant doit etre compris entre 0 et 20</message> </field-validator> 5. date Cette validation permet de vérifier si un champ est de type date et dans une certaine plage. Par contre, le modèle de la date dépend de la locale courante de l application. La validation suivante permet de vérifier si la date saisie n est pas supérieure au 31 décembre <field-validator type="date"> <param name="max">31/12/2000</param> <message>le champ ne doit pas etre superieur au 31/12/2000</message> </field-validator> 6. e mail Cette validation permet de vérifier si un champ de type chaîne de caractères est une adresse e mail valide. Nous allons reprendre notre exemple08 et ajouter un champ e mail associé à son validateur. Code : exemple08.clientaction.java package exemple08; import public class ClientAction extends ActionSupport - 2 -

184 private String identifiant; private String motdepasse; private String ; // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return SUCCESS; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" id="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:textfield name=" " id=" " label=" " labelposition="top" cssclass="input"/> <s:submit value="ajouter un client"/> </s:form> </div> </body> </html> - 3 -

185 Formulaire client et validation de l e mail 7. url Cette validation permet de vérifier si un champ de type chaîne de caractères est une URL correcte. Ce validateur utilise la classe java.net.url pour vérifier la syntaxe. <field-validator type="url"> <message>le champ URL est incorrect</message> </field-validator> 8. regex Cette validation permet de spécifier une expression régulière pour vérifier un champ de formulaire. Ce validateur utilise la classe java.lang.regexp.pattern pour vérifier la syntaxe. <field name="client.codepostal"> <field-validator type="regex"> <param name="expression"><![cdata[^\d5$]]></param> <message>le champ doit être un code postal de la forme NNNNN </field-validator> </field> 9. fieldexpression et expression Ces validations permettent d utiliser des expressions OGNL pour valider les champs de formulaire. Les validations de type fieldexpression utilisent deux paramètres, min et max, permettant de définir une contrainte au niveau des saisies utilisateur. Nous allons montrer cette validation à l aide de deux nouveaux champs pour la gestion du salaire du client. Code : exemple08.clientaction.java package exemple08; import com.opensymphony.xwork2.actionsupport; - 4 -

186 @SuppressWarnings("serial") public class ClientAction extends ActionSupport private String identifiant; private String motdepasse; private String ; private int salairemax=4000; private int salaireclient; // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return SUCCESS; Code : /exemple08/clientaction-validerajouter_client-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" " <validators> <field name="identifiant"> <field-validator type="stringlength"> <param name="minlength">4</param> <param name="maxlength">10</param> <message>le champ identifiant doit avoir entre 4 et 10 caracteres</message> </field-validator> </field> <field name="motdepasse"> <field-validator type="requiredstring"> <message>le champ mot de passe est obligatoire</message> </field-validator> </field> <field name=" "> <field-validator type=" "> <message>le champ est incorrect</message> </field-validator> </field> <field name="salaireclient"> <field-validator type="fieldexpression"> <param name="fieldname">salairemax</param> <param name="expression"> salairemax > salaireclient </param> <message>le salaire du client ne peut pas etre superieur au salaire maxi</message> </field-validator> </field> </validators> - 5 -

187 Formulaire client et gestion d une plage de validation Dans le test, le salaire maximal doit être supérieur au salaire du client (mini) afin de ne pas déclencher d erreur. Les validations de type expression utilisent deux paramètres, min et max, permettant de définir une contrainte au niveau des saisies utilisateur mais contrairement à la validation fieldexpression, ces validations déclenchent une erreur d action (action error) et non pas une erreur de champ (field error). Lors de l utilisation de validations de type expression, la balise Struts <s:action/> permet d afficher les messages d erreurs. 10. conversion Cette validation permet d afficher une erreur lorsque l action rencontre une erreur de conversion. Nous allons reprendre notre exemple et ajouter un champ pour gérer l âge du client sous forme d entier. Code : exemple08.clientaction.java package exemple08; import public class ClientAction extends ActionSupport private String identifiant; private String motdepasse; private int age; // getter et setter // ajouter les informations du client dans la session public String ajouter() // vérifier les saisies, en cas d erreur retourner sur la page de saisie - 6 -

188 if(this.identifiant.equals("") this.motdepasse.equals("")) return "input"; // pas d erreur else return SUCCESS; Code : /exemple08/clientaction-validerajouter_client-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" " <validators> <field name="identifiant"> <field-validator type="stringlength"> <param name="minlength">4</param> <param name="maxlength">10</param> <message>le champ identifiant doit avoir entre 4 et 10 caracteres</message> </field-validator> </field> <field name="motdepasse"> <field-validator type="requiredstring"> <message>le champ mot de passe est obligatoire</message> </field-validator> </field> <field name="age"> <field-validator type="conversion"> <message>le champ age doit etre un entier</message> </field-validator> </field> </validators> Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" id="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:textfield name="age" id="age" label="age" labelposition="top" cssclass="input"/> <s:submit value="ajouter un client"/> </s:form> </div> </body> </html> - 7 -

189 Formulaire client et validation de l âge Le message Invalid field value for field nomduchamp est affiché par défaut par Struts. Ce message peut être surchargé ou modifié dans le fichier de propriétés. Cette technique est expliquée en détail dans le chapitre Gestion des types et conversions. 11. visitor Cette validation permet d améliorer la maintenance et la réutilisabilité d un système si nous souhaitons partager des règles de validation qui seraient utilisées par plusieurs formulaires. Dans notre exemple de formulaire de création d un client, nous utilisons les champs identifiant et motdepasse. Des règles précises seront alors assignées à ces champs pour les formulaires de création et modification. Maintenant, si nous souhaitons appliquer ces même règles au formulaire d authentification qui contiendra également les champs identifiant et mot de passe, nous pouvons utiliser ce validateur. Par exemple, notre définition de l identifiant présente dans le fichier ClientAction ValiderAjouter_Client validation.xml est la suivante : <field name="identifiant"> <field-validator type="stringlength"> <param name="minlength">4</param> <param name="maxlength">10</param> <message>le champ identifiant doit avoir entre 4 et 10 caracteres</message> </field-validator> </field> Maintenant, si nous souhaitons utiliser la même validation pour un champ identifiant dans une autre action, nous utiliserons la définition suivante : <field name="identifiant"> <field-validator type="visitor"> <message>le champ identifiant doit avoir entre 4 et 10 caracteres</message> </field-validator> </field> - 8 -

190 Le lien entre les validations est réalisé par l intermédiaire de l attribut name des balises <field/>

191 Mise en place d un exemple complet Dans l exemple précédent, nous avons utilisé une classe d action ClientAction ainsi que tous ses accesseurs et parfois une classe interne comme Profession. Cette technique bien que tout à fait opérationnelle n est pas très souple et requiert beaucoup de lignes de code dans la classe d action. Nous allons utiliser un nouveau projet exemple09 avec une classe d action ClientAction ainsi que deux JavaBeans, Client.java et Profession.java. Dans la classe d action ClientAction, ce n est plus chaque propriété qui sera déclarée en conformité avec ses accesseurs mais l objet client en lui même. Il n y aura donc plus qu un seul getter et setter pour travailler avec l objet client et pas d accesseurs pour chaque propriété. Les vues seront alors adaptées en conséquence ainsi que les fichiers de validation. Le fichier de propriétés est également utilisé pour la gestion des messages et des langues. Le premier fichier package.properties contient les messages informatifs ainsi que les textes associés aux validations. Le second fichier package_en.properties est identique au premier avec les traductions des messages. Ce fichier de propriétés est évolué et utilise lui même des méthodes Struts afin d afficher dynamiquement le nom des champs concernés par une validation avec la méthode gettext( ). Par exemple, la ligne suivante permet d afficher le message d erreur lors d une validation en faisant le lien avec le nom du champ de façon dynamique. client.identifiant=identifiant champobligatoire=le champ $gettext(fieldname) est obligatoire En cas d erreur sur le champ client.identifiant, la méthode $gettext(fieldname) va alors remplacer le champ associé et va donc afficher le message suivant : Le champ Identifiant est obligatoire. L application permet d ajouter un nouveau client à partir d un identifiant, un mot de passe et du choix de sa profession dans une liste dynamique. Cette liste est retournée par la classe d action, et le mécanisme d accesseur permet de faire le lien entre les différentes propriétés de l application. Enfin, la page JSP /jsp/afficherclient.jsp permet d afficher les saisies du client dans l interface par l intermédiaire de l accès à l objet client. Arborescence du projet exemple09 Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple09" namespace="/" extends="struts-default"> - 1 -

192 <default-action-ref name="ajouter_client" /> <action name="ajouter_client" class="exemple09.clientaction"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="validerajouter_client" class="exemple09.clientaction"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action> </package> </struts> Code : exemple09.clientaction.java package exemple09; import java.util.arraylist; import java.util.list; import com.opensymphony.xwork2.actionsupport; import exemple09.javabeans.client; import public class ClientAction extends ActionSupport // objet JavaBean private Client client; // liste des professions private List<Profession> listeprofessions=new ArrayList<Profession>(); public Client getclient() return client; public void setclient(client client) this.client = client; public List<Profession> getlisteprofessions() listeprofessions.add(new Profession(0,"")); listeprofessions.add(new Profession(1,"Informaticien")); listeprofessions.add(new Profession(2,"Formateur")); listeprofessions.add(new Profession(3,"Administrateur")); listeprofessions.add(new Profession(4,"Programmeur")); return listeprofessions; public void setlisteprofessions(list<profession> listeprofessions) this.listeprofessions = listeprofessions; Code : exemple09.client.java package public class Client - 2 -

193 private String identifiant; private String motdepasse; private Profession profession; public Client() // getter et setter Code : exemple09.profession.java package public class Profession private int idprofession; private String nom; public Profession() public Profession(int idprofession,string nom) this.idprofession=idprofession; this.nom=nom; // getter et setter Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.ajouter )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label><s:property value="%gettext( erreur )"/></label> <ul><s:fielderror/></ul> </div> </s:if> <div id="enveloppe"> <ul> <li><a href="ajouter_client.action? request_locale=fr">français</a></li> <li><a href="ajouter_client.action? request_locale=en">anglais</a></li> </ul> <br/> <h3><s:property value="%gettext( client.ajouter )"/></h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="client.identifiant" id="client.identifiant" label="%gettext( client.identifiant )" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="%gettext( client.motdepasse )" labelposition="top" cssclass="input"/> <s:select name="client.profession.idprofession" id="client.profession.idprofession" label="% gettext( client.profession.idprofession )" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"/> <s:submit value="%gettext( client.ajouter )"/> - 3 -

194 </s:form> </div> </body> </html> Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <div id="enveloppe"> <p> <h4><s:property value="% gettext( client.afficher )"/></h4> <s:property value="% gettext( client.identifiant )"/>: <s:property value="client.identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/ >: <s:property value="client.motdepasse"/><br/> <s:property value="% gettext( client.profession.idprofession )"/>: <s:property value="client.profession.idprofession"/><br/> </p> </div> </body> </html> Formulaire client avec validations - 4 -

195 Gestion des messages d erreurs et succès Les exemples présentés dans les validations et l internationalisation utilisent des collections pour afficher les messages d erreurs lors des validations. Ces messages sont gérés dynamiquement par Struts et permettent d afficher des textes en dur ou insérés dans les fichiers de propriétés. Avec Struts, il existe trois types de messages associés à des collections de données : actionerrors contient la liste des messages d erreurs lors des traitements des actions en association avec la variable errormessages. fielderrors contient la liste des messages d erreurs lors des traitements des validations des champs de formulaires en association avec la variable errors. actionmessages contient la liste des messages de succès lors des traitements. Nous pouvons donc gérer ces trois types de messages avec les fichiers de validation XML mais aussi sous forme logiciel, avec les méthodes de programmation. Pour ajouter une erreur d action actionerrors, la méthode suivante est ajoutée dans les classes d action : addactionerror("texte pour justifier l erreur"); Maintenant, dans la vue il est nécessaire d utiliser la balise Struts adaptée pour afficher les erreurs d action : <s:actionerror/> Pour ajouter une erreur de validation fielderrors, la méthode suivante est ajoutée dans les classes d action : addfielderror("champ", "Texte pour justifier l erreur de validation"); Maintenant, dans la vue il est nécessaire d utiliser la balise Struts adaptée pour afficher les erreurs d action : <s:fielderror/> Pour ajouter un message de succès actionmessages, la méthode suivante est ajoutée dans les classes d action : addactionmessage("texte pour justifier le succès"); Maintenant, dans la vue il est nécessaire d utiliser la balise Struts adaptée pour afficher les messages de succès : <s:actionmessage/> Nous allons améliorer notre projet exemple09 afin de tester si la création du client correspond à l identifiant jlafosse. Dans le cas contraire, un message d erreur dynamique sera ajouté ainsi qu un autre sur le champ client.identifiant sinon un message de succès sera affiché. Les vues sont modifiées pour afficher les messages adaptés. La classe d action ClientAction possède une nouvelle méthode verifier() paramétrée dans le fichier de configuration struts.xml. Code : struts.xml... <action name="validerajouter_client" class="exemple09.clientaction" method="verifier"> <result name="input">/jsp/ajouterclient.jsp</result> <result name="success">/jsp/afficherclient.jsp</result> </action>... Code : exemple09.clientaction.java package exemple09; import java.util.arraylist; import java.util.list; import com.opensymphony.xwork2.actionsupport; - 1 -

196 import exemple09.javabeans.client; import public class ClientAction extends ActionSupport // méthode appelée après vérification des saisies par le validateur public String verifier() // vérifier si l identifiant est correct if(!this.client.getidentifiant().equals("jlafosse")) addfielderror("client.identifiant", gettext("champlogin")); addactionerror(gettext("erreuraction.login")); return "input"; // pas d erreur else addactionmessage(gettext("succes")); return SUCCESS; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="% gettext( client.ajouter )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label><s:property value="%gettext( erreur )"/></label> <ul><s:fielderror/></ul> </div> </s:if> <!-- Message d erreur lors des actions --> <s:if test="errormessages.size()>0"> <div id="message_erreur"> <label><s:property value="% gettext( erreuraction )"/></label> <ul><s:actionerror/></ul> </div> </s:if> <div id="enveloppe"> <ul> <li><a href="ajouter_client.action? request_locale=fr">français</a></li> <li><a href="ajouter_client.action? request_locale=en">anglais</a></li> </ul> <br/> <h3><s:property value="%gettext( client.ajouter )"/></h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="client.identifiant" id="client.identifiant" label="%gettext( client.identifiant )" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="%gettext( client.motdepasse )" labelposition="top" cssclass="input"/> - 2 -

197 <s:select name="client.profession.idprofession" id="client.profession.idprofession" label="% gettext( client.profession.idprofession )" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"/> <s:submit value="%gettext( client.ajouter )"/> </s:form> </div> </body> </html> Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <!-- Message de succès --> <s:if test="actionmessages.size()>0"> <div id="message_information"> <ul><s:actionmessage/></ul> </div> </s:if> <div id="enveloppe"> <p> <h4><s:property value="% gettext( client.afficher )"/></h4> <s:property value="% gettext( client.identifiant )"/>: <s:property value="client.identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/ >: <s:property value="client.motdepasse"/><br/> <s:property value="% gettext( client.profession.idprofession )"/>: <s:property value="client.profession.idprofession"/><br/> </p> </div> </body> </html> Code : package.properties client.ajouter=ajouter un client client.identifiant=identifiant client.motdepasse=mot de passe client.profession.idprofession=profession du client client.afficher=afficher un client erreur=les erreurs suivantes se sont produites : erreuraction=les erreurs d actions suivantes se sont produites : champobligatoire=le champ $gettext(fieldname) est obligatoire champminimum=le champ $gettext(fieldname) doit avoir entre $ gettext(minlength) et $gettext(maxlength) caractères champlogin=le champ n est pas associé à un compte erreuraction.login=le champ n est pas associé à un compte correct pour l application succes=le formulaire a été complété avec succès - 3 -

198 Formulaire client et validations avancées multilingues Affichage client après validation Les messages d erreurs sont affichés par l intermédiaire de la balise <s:fielderror/> dans notre boîte en haut de la page mais ils sont également automatiquement affichés à proximité de chaque champ du formulaire. Cet affichage est géré par le thème de la page par défaut xhtml. Dans notre formulaire de saisie, le thème n est pas précisé, c est donc le thème par défaut qui est utilisé à savoir xhtml. Avec ce mode d affichage sous forme de tableau HTML, les messages d erreurs sont automatiquement associés aux champs. Pour désactiver ce service, nous pouvons changer de thème et utiliser le thème simple qui ne réalise pas de mise en page. Nous précisons ce thème dans la balise <s:form/> de notre page JSP AjouterClient.jsp. <s:form method="post" action="validerajouter_client" theme="simple"> - 4 -

199 Formulaire client et thème simple - 5 -

200 Écrire un validateur Les validateurs proposés en standard par Struts sont largement suffisants pour la plupart des applications web. Cependant, il est parfois nécessaire de développer des validateurs spécifiques en fonction d un besoin particulier. Un validateur Struts implémente l interface Validator définie dans le paquetage com.opensymphony.xwork2.validator. Struts utilise également un intercepteur pour la gestion des validations. Le validateur s occupe du chargement et de l exécution des validateurs. L intercepteur, qui pour rappel fonctionne comme un filtre, déclenche la méthode setvalidatorcontext() pour assigner le validateur dans le contexte adapté. Dans un second temps, l intercepteur déclenche la méthode validate(object objetavalider) avec en paramètre l objet à valider. Cette méthode est responsable du traitement de la validation et c est celle la que nous devons surcharger pour réaliser nos validations. 1. L interface Validator et les classes ValidatorSupport et FieldValidatorSupport Pour pouvoir surcharger la méthode validate(object objetavalider), il est plus simple d hériter de la classe ValidatorSupport ou FieldValidatorSupport plutôtque d implémenter l interface Validator. La classe ValidatorSupport est utilisée pour définir des validations complexes (de type action) et la classe FieldValidator est héritée pour les validations de champs de formulaires. Si notre validateur doit utiliser des paramètres, le principe est identique aux classes d action. Il suffit en effet de déclarer la variable dans la classe validateur et de définir ses getter et setter. La classe ValidatorSupport propose plusieurs méthodes pour la gestion des validations : getfieldvalue(string nom, Object objet) : permet de retourner la valeur d un champ précisé. addactionerror(object actionerror) : permet d ajouter une erreur de type action. addfielderror(string nom, Object objet) : permet d ajouter une erreur de champ. 2. Déclarer les validateurs Les validations sont déclarées en standard dans le fichier default.xml présent dans la classe suivante : com.opensymphony.xwork2.validator.validators présente dans l archive xwork.jar. Lors de la création de nouveaux validateurs, il est nécessaire de déclarer ces validateurs dans un fichier validators.xml présent dans le répertoire /WEB INF/classes ou /WEB INF/src de l application. Voici le contenu du fichier com.opensymphony.xwork2.validator.validators.default.xml livré en standard avec Struts : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator Config 1.0//EN" " 1.0.dtd"> <!-- START SNIPPET: validators-default --> <validators> <validator name="required" class="com.opensymphony.xwork2.validator.validators.requiredfieldv alidator"/> <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.requiredstring Validator"/> <validator name="int" class="com.opensymphony.xwork2.validator.validators.intrangefieldv alidator"/> <validator name="long" class="com.opensymphony.xwork2.validator.validators.longrangefield Validator"/> <validator name="short" class="com.opensymphony.xwork2.validator.validators.shortrangefiel dvalidator"/> <validator name="double" class="com.opensymphony.xwork2.validator.validators.doublerangefie - 1 -

201 ldvalidator"/> <validator name="date" class="com.opensymphony.xwork2.validator.validators.daterangefield Validator"/> <validator name="expression" class="com.opensymphony.xwork2.validator.validators.expressionvali dator"/> <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.fieldexpressio nvalidator"/> <validator name=" " class="com.opensymphony.xwork2.validator.validators. validator "/> <validator name="url" class="com.opensymphony.xwork2.validator.validators.urlvalidator"/ > <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.visitorfieldva lidator"/> <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.conversionerro rfieldvalidator"/> <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.stringlengthfi eldvalidator"/> <validator name="regex" class="com.opensymphony.xwork2.validator.validators.regexfieldvali dator"/> <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.conditionalvis itorfieldvalidator"/> </validators> <!-- END SNIPPET: validators-default --> 3. Mise en application Nous allons utiliser un nouveau projet exemple10 à partir de l exemple précédent pour mettre en place un validateur. Nous allons définir un validateur complexe pour l identifiant du client. Cet identifiant doit avoir une taille minimale, une taille maximale et ne doit pas être déjà utilisé. Pour cela, le validateur va vérifier si l identifiant n est pas dans une liste. Bien entendu, lors d une mise en application professionnelle, cette liste pourrait être présente dans une base de données plutôt que de façon constante dans le code. Nous commençons par créer une classe nommée ValidateurIdentifiant héritant de la classe FieldValidatorSupport. Afin de conserver un système homogène, nous allons créer un paquetage dédié aux validateurs nommé exemple10.validator. Le fichier de validation validators.xml est placé dans le répertoire /WEB INF/src de l application et contient la déclaration du validateur. Maintenant que nous avons enregistré le validateur sous le nom validateuridentifiant, nous pouvons l utiliser dans les fichiers de validation de la même façon que n importe quel validateur. Code : /WEB-INF/src/validators.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator Config 1.0//EN" " <validators> <validator name="validateuridentifiant" class="exemple10.validator.validateuridentifiant"/> </validators> La classe de validation ValidateurIdentifiant contient deux paramètres nommés minlength et maxlength qui seront passés par le fichier de validation ClientAction ValiderAjouter_Client validation.xml avec les balises <param name="minlength"/> et <param name="maxlength"/>. Ensuite, la méthode validate(object objetavalider) qui est déclenchée à chaque validation, vérifie si l identifiant n est pas dans la liste suivante : jlafosse, astapane, crenault, amartin. Ces identifiants ne pourront donc pas être utilisés pour valider le formulaire

202 Arborescence du projet exemple10 Code : /exemple10/clientaction-validerajouter_client-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" " <validators> <field name="client.identifiant"> <field-validator type="requiredstring"> <message key="champobligatoire"/> </field-validator> </field> <field name="client.identifiant"> <field-validator type="validateuridentifiant"> <param name="minlength">4</param> <param name="maxlength">20</param> <message key="validateuridentifiant"/> </field-validator> </field> <field name="client.motdepasse"> <field-validator type="requiredstring"> <message key="champobligatoire"/> </field-validator> </field> <field name="client.profession.idprofession"> <field-validator type="int"> <param name="min">1</param> <message key="champobligatoire"/> </field-validator> </field> </validators> Code : exemple10.validator.validateuridentifiant package exemple10.validator; import java.util.arraylist; import java.util.list; import com.opensymphony.xwork2.validator.validationexception; import com.opensymphony.xwork2.validator.validators.fieldvalidatorsupport; - 3 -

203 public class ValidateurIdentifiant extends FieldValidatorSupport private int minlength=0; private int maxlength=0; public int getminlength() return minlength; public void setminlength(int minlength) this.minlength = minlength; public int getmaxlength() return maxlength; public void setmaxlength(int maxlength) this.maxlength = maxlength; // validation à réaliser sur le champ public void validate(object objet) throws ValidationException String nomchamp=this.getfieldname(); String valeurchamp=(string)this.getfieldvalue(nomchamp, objet); caractères // vérifier la taille mini de l identifiant if(valeurchamp.length() < this.minlength) addfielderror(nomchamp,objet); return; if(valeurchamp.length() > this.maxlength) addfielderror(nomchamp,objet); return; // vérifier que l identifiant ne contient que ces if(isutiliseidentifiant(valeurchamp)) addfielderror(nomchamp,objet); return; // vérifier si l identifiant n est pas déjà utilisé public boolean isutiliseidentifiant(string identifiant) List<String> listeidentifiants=new ArrayList<String>(); listeidentifiants.add("jlafosse"); listeidentifiants.add("astapane"); listeidentifiants.add("crenault"); listeidentifiants.add("amartin"); liste"); liste"); if(listeidentifiants.contains(identifiant)) System.out.println(identifiant+" est dans la return true; System.out.println(identifiant+" n est pas dans la return false; - 4 -

204 Code : exemple10.clientaction.java package exemple10; import java.util.arraylist; import java.util.list; import com.opensymphony.xwork2.actionsupport; import exemple10.javabeans.client; import public class ClientAction extends ActionSupport // objet JavaBean private Client client; // liste des professions private List<Profession> listeprofessions=new ArrayList<Profession>(); // getter et setter // méthode pour ajouter un message de succès public String verifier() addactionmessage(gettext("succes")); return SUCCESS; Code : exemple10.package.properties validateuridentifiant=le champ $gettext(fieldname) doit avoir entre $gettext(minlength) et $gettext(maxlength) caractères et ne doit pas être utilisé Code : exemple10.package_en.properties validateuridentifiant=the field $gettext(fieldname) must be between $gettext(minlength) and $gettext(maxlength) characters and must not be used - 5 -

205 Formulaire client multilingue avec validations personnelles Affichage client après validation 4. Validation dans les classes d action Le paragraphe précédent présente la mise en application d une validation XML pour les champs utilisés de façon déclarative. Struts propose également des validations logiciel sous forme de code Java qui peut être intégré aux classes d actions. L interface com.opensymphony.xwork2.validateable peut être implémentée dans les classes d action pour fournir une validation par programmation. Cette interface propose une seule signature de méthode nommée - 6 -

206 validate(). La méthode validate() contient toute la logique de validation des propriétés des JavaBeans. Si une classe d action implémente l interface Validateable, la méthode validate() va alors être automatiquement déclenchée. Si notre classe d action hérite de la classe ActionSupport, nous n avons pas besoin d implémenter l interface Validateable. Nous allons utiliser cette technique pour imposer l identifiant adurand lors de la création d un compte client. Dans le cas contraire, un message de type fielderror sera ajouté. Struts offre une programmation souple et permet de combiner les techniques de validation. Dans notre exemple, nous avons utilisé des validations XML (bundle validation) par définition, un validateur spécifique et enfin une validation en programmation, avec la méthode validate() dans la classe d action. Code : exemple10.clientaction.java package exemple10; import java.util.arraylist; import java.util.list; import com.opensymphony.xwork2.actionsupport; import exemple10.javabeans.client; import public class ClientAction extends ActionSupport // objet JavaBean private Client client; // liste des professions private List<Profession> listeprofessions=new ArrayList<Profession>(); // getter et setter // méthode pour vérifier que l identifiant utilisé est bien adurantd public void validate() if(client!=null) if(! client.getidentifiant().equals("adurand")) addfielderror("identifiantutilisateur",gettext("identifiantutilisa teur")); // méthode pour ajouter un message de succès public String verifier() addactionmessage(gettext("succes")); return SUCCESS; Code : exemple10.package.properties identifiantutilisateur=le champ Identifiant doit être "adurand" Code : exemple10.package_en.properties identifiantutilisateur=the field Login must be "adurand" - 7 -

207 Formulaire client avec validation personnelle de l identifiant - 8 -

208 En résumé Ce chapitre a présenté la mise en place de validations au sein d une application Struts. Pour cela, le framework offre plusieurs outils à partir d écriture de fichiers XML, de création de validateurs personnels et dans les cas les plus complexes, la mise en place de validations logicielles en programmation. Struts propose également de coupler ces trois techniques pour des projets de grande envergure

209 Présentation Dans les chapitres précédents, nous avons étudié comment récupérer les données saisies dans les formulaires et mettre en place des validations plus ou moins complexes. Chaque paramètre transmis par l intermédiaire du protocole HTTP est considéré comme une chaîne de caractères de type String. Par exemple, la saisie de l âge du client dans le formulaire sera bien vérifiée par les validateurs comme étant un entier mais sera reçue comme une chaîne de caractères. Pour éviter les conversions multiples et laborieuses, Struts propose de mettre en place des services simples de conversions

210 Gestion des conversions Struts utilise un intercepteur nommé paramsresponsable de réaliser le mapping entre les champs des formulaires et leurs accesseurs respectifs. Grâce à cet intercepteur, les chaînes de caractères reçues par le protocole HTTP sont automatiquement converties vers le type défini dans la classe d action, comme dans notre exemple précédent sur la gestion du compte client. L id de la profession du client est bien reçu sous la forme d une chaîne de caractères mais il est automatiquement converti en entier. Struts réalise des conversions entre les différents types et un problème de conversion (ex : caractère vers entier) ne va pas nécessairement stopper le framework. L interface com.opensymphony.xwork2.validationaware est responsable de la gestion des conversions de types. Si notre classe d action n implémente pas cette interface, des exceptions peuvent être déclenchées. Les messages d erreurs de conversions sont gérés par le fichier de propriétés. La saisie d une valeur incorrecte au niveau du type va déclencher un message de la forme : Invalid field value for field fieldname. Nous avons déjà rencontré ce type de message lors du projet exemple08 lorsque nous avons saisi un caractère pour l âge du client. Nous pouvons modifier les affichages concernant les types, en surchargeant ces messages par défaut dans notre fichier de propriétés. Dans notre cas, nous pouvons utiliser cette définition dans le fichier package.properties. Invalid.fieldvalue.NomDuChamp=Erreur de type dans un champ du formulaire Le paramètre NomDuChamp correspond au nom du champ utilisé dans les formulaires et validations. Nous pouvons reprendre notre application exemple08 et l utiliser pour notre nouveau projet exemple11. Arborescence de l exemple11 Code : exemple11.package.properties invalid.fieldvalue.age=le champ age doit être un entier pour valider le formulaire - 1 -

211 Formulaire d ajout client et gestion des types Le fichier de validation contenant les messages (exemple11.package.properties) peut également être nommé suivant le nom de la classe, et positionné au même endroit que celle ci dans l arborescence du projet. Dans notre exemple, nous aurions pu utiliser le fichier exemple11.clientaction.properties

212 Gestion des types Comme nous l avons précisé dans le paragraphe précédent, Struts propose des convertisseurs de types simples qui peuvent être largement utilisés dans la plupart des projets. Cependant, les conversions de types complexes ne sont pas envisageables avec le système proposé en standard. Pour cela, nous devons créer nos propres convertisseurs de types. Un convertisseur de types doit implémenter l interface ognl.typeconverter ou hériter de la classe DefaultTypeConverter ou StrutsTypeConverter. Cette interface possède une seule signature de méthode nommée convertvalue( ) permettant de gérer la conversion vers le type adapté. L implémentation de cette méthode permet de récupérer la propriété concernée par le type ainsi que la classe de la propriété

213 Mise en application La mise en application des conversions automatiques de types nécessite une configuration préalable. Pour cela, nous devons créer un fichier qui doit contenir le nom de la classe d action suffixé par le terme conversion. Pour notre exemple de la classe ClientAction, le fichier de conversion sera alors placé dans le même répertoire que la classe et aura pour nom ClientAction conversion.properties. Le fichier de configuration doit être placé dans le même répertoire que la classe d action. Ce fichier de propriétés contient les noms des champs qui doivent être associés à des conversions de types ainsi que la classe associée à la vérification du type. Les classes de gestion de types, associées à une action sont toujours déclenchées même dans le cas de l utilisation de validations. Notre classe de validation du type pour le champ datenaissance sera donc déclenchée par l intercepteur en même temps que le setter de la propriété concernée. Pour mettre en application la gestion des types et conversions nous allons reprendre notre projet exemple10 basé sur la gestion des comptes client et ajouter une propriété datenaissance de type java.util.date. Arborescence du projet exemple12 Cette application contient un nouveau champ pour la date de naissance du client. Le fichier de propriétés ClientActionconversion.properties permet de faire le lien entre le champ à vérifier et la classe chargée de réaliser cette opération. Code : exemple12.clientaction-conversion.properties client.datenaissance=exemple12.convertisseur.datenaissance Convertisseur La classe permettant de gérer la conversion de type est simple, elle récupère les données saisies et réalise une conversion vers un modèle donné (en français dans ce cas). Si la conversion est correcte, elle retourne le résultat dans l objet concerné, dans le cas contraire elle sort du convertisseur sans bloquer l application. Les saisies correctes sont traitées et renvoyées dans le formulaire alors que les saisies incorrectes ne sont pas renvoyées dans le formulaire. Code : exemple12.convertisseur.datenaissanceconvertisseur package exemple12.convertisseur; import java.text.dateformat; import java.text.parseexception; import java.text.simpledateformat; - 1 -

214 import java.util.date; import java.util.map; import ognl.defaulttypeconverter; public class DateNaissanceConvertisseur extends DefaultTypeConverter public Object convertvalue(map context, Object valeur, Class classe) System.out.println("Dans le convertisseur"); // est-ce que la saisie est bien un type Date if (classe == Date.class) DateFormat dateformat=new SimpleDateFormat("dd/MM/yyyy"); dateformat.setlenient(false); try String[] s=(string[])valeur; Date date=dateformat.parse(s[0]); System.out.println("dans la date : "+dateformat.format(date)); return date; catch(parseexception e) //System.out.println("Error:" + e); return null; Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.ajouter )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label><s:property value="%gettext( erreur )"/></label> <ul><s:fielderror/></ul> </div> </s:if> <!-- Message d erreur lors des actions --> <s:if test="errormessages.size()>0"> <div id="message_erreur"> <label><s:property value="% gettext( erreuraction )"/></label> <ul><s:actionerror/></ul> </div> </s:if> <div id="enveloppe"> <ul> <li><a href="ajouter_client.action? request_locale=fr">français</a></li> <li><a href="ajouter_client.action? request_locale=en">anglais</a></li> </ul> <br/> <h3><s:property value="%gettext( client.ajouter )"/></h3> <s:form method="post" action="validerajouter_client"> <s:textfield name="client.identifiant" - 2 -

215 id="client.identifiant" label="%gettext( client.identifiant )" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="%gettext( client.motdepasse )" labelposition="top" cssclass="input"/> <s:textfield name="client.datenaissance" id="client.datenaissance" label="% gettext( client.datenaissance )" labelposition="top" cssclass="input" /> <s:select name="client.profession.idprofession" id="client.profession.idprofession" label="% gettext( client.profession.idprofession )" labelposition="top" list="listeprofessions" listkey="idprofession" listvalue="nom"/> <s:submit value="%gettext( client.ajouter )"/> </s:form> </div> </body> </html> Code : /jsp/afficherclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title><s:property value="%gettext( client.afficher )"/></title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <s:debug/> <!-- Message de succès --> <s:if test="actionmessages.size()>0"> <div id="message_information"> <ul><s:actionmessage/></ul> </div> </s:if> <div id="enveloppe"> <p> <h4><s:property value="% gettext( client.afficher )"/></h4> <s:property value="% gettext( client.identifiant )"/>: <s:property value="client.identifiant"/> <br/> <s:property value="%gettext( client.motdepasse )"/ >: <s:property value="client.motdepasse"/><br/> <s:property value="% gettext( client.profession.idprofession )"/>: <s:property value="client.profession.idprofession"/><br/> <s:property value="% gettext( client.datenaissance )"/>: <s:date name="client.datenaissance" format="dd/mm/yyyy"/> </p> </div> </body> </html> - 3 -

216 Formulaire d ajout client exemple12 Affichage du compte client - 4 -

217 En résumé Le protocole HTTP utilise uniquement des chaînes de caractères pour transmettre des informations aux applications. Cette contrainte de type nécessite alors des conversions (chaînes de caractères vers entier, date ou objet). Struts propose une gestion automatique des conversions pour les types simples à partir des chaînes de caractères reçues. Par contre, nous pouvons définir nos propres classes de conversions et de gestion des types à partir d une nomenclature précise et efficace

218 Présentation La plupart des applications web utilisent une base de données pour la persistance des informations. Les sites de commerce par exemple, stockent les informations concernant les clients, articles ou commandes. La plupart des bases de données commercialisées sont de nature client serveur et recourent au langage SQL pour manipuler les données qu elles contiennent. Java possède une API pour travailler avec les bases de données.cette technologie nommée JDBC (Java DataBase Connectivity) est une bibliothèque d interfaces et de classes, utilisée pour accéder aux SGBDR (Système de Gestion de Base de Données Relationnel). JDBC fournit aux développeurs tous les outils nécessaires pour permettre à des programmes client de se connecter à des bases de données et de leur envoyer des requêtes. Ces requêtes sont écrites en langage SQL. Les applications modernes Java EE utilisent le modèle de conception MVC (Modèle Vue Contrôleur). Nous avons étudié jusqu ici la partie Vue avec les pages JSP et la partie Contrôleur avec les classes d action, il nous reste la partie persistance et accès aux données avec la couche Modèle. Architecture MVC Les applications sont donc découpées en plusieurs couches ou tiers, afin de séparer chaque fonctionnalité du système. La communication entre deux tiers (vue vers contrôleur, contrôleur vers modèle et vice versa) repose sur l invocation des accesseurs (getter et setter) et le passage des paramètres adaptés. Struts est considéré comme un framework de présentation et s occupe essentiellement des couches Vue et Contrôleur. La mise en place de la couche modèle est alors du ressort du développeur. La dernière couche sera donc responsable de la persistance des objets utilisés ou de l accès au SGBD pour transformer les données objet en données relationnelles. Cette technique est appelée mapping relationnel vers objet. La technique inverse qui consiste à transformer un objet en données dans une base de données relationnelle est appelée mapping objet vers relationnel, elle est réalisée par l intermédiaire de requêtes SQL. Par convention, une classe d action est suffixée par le terme Action (par exemple : ClientAction.java), elle est associée à sa classe modèle suffixée ou préfixée par le terme Modele (par exemple : ClientModele.java ou ModeleClient.java). Ces deux classes manipulent un JavaBean correspondant à l objet lui même (ses attributs et méthodes) (par exemple : Client.java). La classe d action est considérée comme le contrôleur et réalise tous les traitements importants d une application. La classe modèle réalise tous les traitements d accès ou de modification des données (par exemple : consulter, ajouter, modifier, supprimer et lister)

219 Mise en application Nous allons utiliser le modèle MVC avec l exemple du formulaire client réduit à l identifiant et au mot de passe. La couche modèle sera réalisée/simulée par une classe statique qui contient une liste dynamique de comptes clients et pourra être mise à jour. Le nouveau projet exemple13 utilise la classe JavaBean Client pour gérer l identifiant et le mot de passe. Cette classe est utilisée dans la classe d action ClientAction et dans le modèle ClientModele. L arborescence du projet est la suivante : Arborescence du projet exemple13 Le fichier de configuration de l application struts.xml possède deux actions, une pour la création d un nouveau compte (Ajouter_Client.action) et une autre pour l affichage des informations (Lister_Client.action). L action Lister_Client.action déclenche la méthode lister() de la classe d action et l action Ajouter_Client.action est associée à la méthode ajouter() de la classe d action. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple13" namespace="/" extends="strutsdefault"> <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple13.clientaction" method="lister"> <result>/jsp/listerclient.jsp</result> </action> <action name="ajouter_client" class="exemple13.clientaction" method="ajouter"> <result name="input">/jsp/listerclient.jsp</result> <result name="success" type="redirectaction">lister_client</result> </action> - 1 -

220 </package> </struts> La classe d action est simple et possède uniquement ses accesseurs et deux méthodes utiles que sont lister() et ajouter(). La méthode lister() récupère les informations présentes dans le modèle pour les assigner à la collection qui sera affichée dans la vue par l intermédiaire de son getter. Code : exemple13.clientaction.java package exemple13; import java.util.list; import com.opensymphony.xwork2.actionsupport; import exemple13.javabeans.client; import public class ClientAction extends ActionSupport private Client client; private List<Client> listeclients; public Client getclient() return client; public void setclient(client client) this.client = client; public List<Client> getlisteclients() listeclients=clientmodele.getlisteclients(); return listeclients; public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; // retourner la liste des clients après récupération public String lister() listeclients=clientmodele.getlisteclients(); return SUCCESS; // ajouter le client dans le modèle public String ajouter() ClientModele.ajouter(this.client); return SUCCESS; La classe JavaBean Client très simple est composée de ses attributs et accesseurs. Code : exemple13.javabeans.client.java package public class Client private int idclient; private String identifiant; private String motdepasse; public Client() - 2 -

221 public Client(int idclient,string identifiant, String motdepasse) this.idclient=idclient; this.identifiant=identifiant; this.motdepasse=motdepasse; // accesseurs de la classe Enfin, le modèle permet de retourner la liste des clients déjà créés et d ajouter un nouveau client à la liste. Cette étape utilise une liste statique mais devrait être remplacée par une base de données dans un projet évolué. Cette classe modèle utilise une liste statique qui sera donc partagée par l ensemble des objets utilisateur. Code : exemple13.modele.clientmodele.java package exemple13.modele; import java.util.arraylist; import java.util.list; import exemple13.javabeans.client; public class ClientModele private static List<Client> listeclients; private static int id=0; static listeclients=new ArrayList<Client>(); listeclients.add(new Client(id++, "jlafosse", "jerome")); listeclients.add(new Client(id++, "astapane", "amelie")); listeclients.add(new Client(id++, "amartin", "alain")); listeclients.add(new Client(id++, "pleroy", "pierre")); // retourner la liste des clients public static List<Client> getlisteclients() return listeclients; public static void setlisteclients(list<client> listeclients) ClientModele.listeClients = listeclients; // ajouter un client dans la liste public static void ajouter(client client) client.setidclient(id++); listeclients.add(client); Le projet est accessible à cette adresse et affiche directement, grâce à la méthode lister() de la classe d action et la ligne listeclients=clientmodele.getlisteclients(), la liste des clients présents dans le modèle. Nous pouvons maintenant ajouter un compte correct et valider la création, la liste sera alors automatiquement mise à jour

222 Gestion des clients L utilisation du type redirectaction dans le résultat de l ajout <result name="success" type="redirectaction">lister_client</result> permet de rediriger totalement le formulaire vers l action Lister_Client.action et ainsi d éviter les doubles créations en cas de rafraîchissement de la page (F5) ou de double clic sur le bouton d ajout. De même, ce type étant une véritable redirection, tous les paramètres sont perdus et les saisies de l utilisateur (identifiant et mot de passe) sont alors vidées. Maintenant, si nous essayons de créer un compte avec une erreur de saisie (ex. : sans entrer de mot de passe), nous remarquons que les saisies sont conservées mais que la liste des clients n est pas affichée. Ceci est tout à fait normal car l action lister() de la classe d action retournant la liste depuis le modèle n est pas déclenchée en cas d erreur de saisie. Les données ne sont donc plus affichées. L intercepteur Preparable permet d éviter ce problème

223 L intercepteur Preparable L intercepteur Preparable appelle la méthode prepare() à chaque fois que la classe d action est déclenchée, si cet intercepteur est associé à l action bien entendu. Pour cela, la classe d action doit implémenter l interface com.opensymphony.xwork2.preparable. Nous allons mettre en place un service complet nommé exemple14 de listing, création, modification et suppression des clients à partir de cet intercepteur. Arborescence projet exemple14 Nous commençons par modifier le fichier de déclaration des actions struts.xml. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple14" namespace="/" extends="strutsdefault"> <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple14.clientaction" method="lister"> <result>/jsp/listerclient.jsp</result> </action> <action name="ajouter_client" class="exemple14.clientaction" method="ajouter"> <result name="input">/jsp/listerclient.jsp</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="editer_client" class="exemple14.clientaction" method="editer"> <result name="success">/jsp/editerclient.jsp</result> </action> - 1 -

224 <action name="modifier_client" class="exemple14.clientaction" method="modifier"> <result name="input">/jsp/editerclient.jsp</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="supprimer_client" class="exemple14.clientaction" method="supprimer"> <result name="success" type="redirectaction">lister_client</result> </action> </package> </struts> Cinq actions sont déclarées et permettent d effectuer des tâches bien spécifiques : Lister_Client.action : cette action permet de déclencher la méthode lister() de la classe d action pour récupérer toute la liste des clients par l intermédiaire du modèle. Cette action réalise une redirection vers la vue ListeClient.jsp pour afficher le formulaire de création et la liste des comptes client. Ajouter_Client.action : cette action permet de déclencher la méthode ajouter() de la classe d action et va ajouter directement l objet client (renseigné par l intercepteur params) dans le modèle. Cette action utilise le fichier de validation ClientAction Ajouter_Client validation.xml et réalise une redirection complète suite à une création vers l action de listing des clients. Editer_Client.action : cette action permet d afficher le formulaire d édition (modification) d un compte client. Pour cela, cette action utilise un paramètre nommé idclienencours passé dans le lien et qui correspond à l id du client à éditer. Modifier_Client.action : cette action permet de modifier l objet client par l intermédiaire du modèle en utilisant les nouvelles valeurs saisies dans le formulaire. Cette action utilise comme pour le formulaire de création, un fichier de validation des saisies, nommé ClientAction Modifier_Client validation.xml. Nous remarquons alors la souplesse de Struts qui permet d utiliser des validations différentes suivant l action à réaliser (création ou modification). Bien sûr, si les validations sont identiques, nous pouvons utiliser un visitor comme expliqué dans le chapitre Bibliothèque de tags Struts consacré aux validations. Cette action réalise une redirection complète vers l action de création d un nouveau compte et de listing des clients. Supprimer_Client.action : cette action permet de supprimer un client à partir du paramètre idclientencours correspond à l id du client concerné par la suppression. En cas de suppression, cette action redirige vers la page de listing. Code : exemple14.clientaction.java package exemple14; import java.util.list; import com.opensymphony.xwork2.actionsupport; import com.opensymphony.xwork2.modeldriven; import com.opensymphony.xwork2.preparable; import exemple14.javabeans.client; import public class ClientAction extends ActionSupport implements Preparable, ModelDriven private Client client; private List<Client> listeclients; private int idclientencours; public void prepare() throws Exception // en création, créer un nouvel objet vide if(idclientencours==0) client=new Client(); - 2 -

225 // en modification, retourner les infos de l objet else client=clientmodele.getclient(idclientencours); public Object getmodel() return client; public int getidclientencours() return idclientencours; public void setidclientencours(int idclientencours) this.idclientencours = idclientencours; public Client getclient() return client; public void setclient(client client) this.client = client; public List<Client> getlisteclients() listeclients=clientmodele.getlisteclients(); return listeclients; public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; // retourner la liste des clients après récupération public String lister() listeclients=clientmodele.getlisteclients(); return SUCCESS; // ajouter le client dans le modèle public String ajouter() ClientModele.ajouter(client); return SUCCESS; // afficher le formulaire en édition public String editer() return SUCCESS; // modifier un client public String modifier() ClientModele.modifier(client); return SUCCESS; // supprimer un client à partir du paramètre reçu nommé idclient public String supprimer() ClientModele.supprimer(idClientEnCours); - 3 -

226 return SUCCESS; Code : exemple14.modele.clientmodele.java package exemple14.modele; import java.util.arraylist; import java.util.list; import exemple14.javabeans.client; public class ClientModele private static List<Client> listeclients; private static int id=1; static listeclients=new ArrayList<Client>(); listeclients.add(new Client(id++, "jlafosse", "jerome")); listeclients.add(new Client(id++, "astapane", "amelie")); listeclients.add(new Client(id++, "amartin", "alain")); listeclients.add(new Client(id++, "pleroy", "pierre")); // retourner la liste des clients public static List<Client> getlisteclients() return listeclients; public static void setlisteclients(list<client> listeclients) ClientModele.listeClients = listeclients; // ajouter un client dans la liste public static void ajouter(client client) client.setidclient(id++); listeclients.add(client); // supprimer un client dans la liste public static void supprimer(int idclient) for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) listeclients.remove(c); // modifier un client dans la liste public static void modifier(client client) int idclient=client.getidclient(); for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) c.setidentifiant(client.getidentifiant()); c.setmotdepasse(client.getmotdepasse()); break; - 4 -

227 // trouver un client dans la liste public static Client getclient(int idclient) for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) return c; return null; Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="ajouter_client"> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="ajouter un client"/> </s:form> <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot de passe</b></td><td colspan="2" align="center"><b>gestion</b></td></tr> <s:iterator value="listeclients" status="ligne"> <s:if test="#ligne.odd"><tr class="ligne1"></s:if> <s:if test="#ligne.even"><tr class="ligne2"></s:if> <td><s:property value="idclient"/></td> <td><s:property value="identifiant"/></td> <td><s:property value="motdepasse"/></td> <td align="center"><a href="editer_client.action? idclientencours=$idclient"/><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="supprimer_client.action? idclientencours=$idclient"/><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> </s:iterator> </table> </div> </body> </html> Code : /jsp/editerclient.jsp - 5 -

228 taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>editer un client</title> <style url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <div id="enveloppe"> <h3>editer un client</h3> <s:form method="post" action="modifier_client"> <s:hidden key="client.idclient"/> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="modifier un client"/> </s:form> </div> </body> </html> La gestion des clients est maintenant terminée. Nous remarquons que les services d affichage, d ajout et de suppression fonctionnent correctement mais pas le service d édition. En effet, lors d un clic sur le lien d édition/modification, le formulaire est vide. Ceci est tout à fait normal, la méthode prepare() de notre classe d action permettant de rechercher le client concerné par idclientencours n est pas déclenchée. Pour déclencher cette méthode automatiquement à chaque appel de l action, il est nécessaire de mettre en place l intercepteur Preparable. La mise en place est effectuée par la ligne suivante <interceptor ref name="paramsprepareparamsstack"/> dans le fichier de configuration struts.xml. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts>... <action name="editer_client" class="exemple14.clientaction" method="editer"> <interceptor-ref name="paramsprepareparamsstack"/> <result name="success">/jsp/editerclient.jsp</result> </action>... </struts> Désormais notre application exemple14 est totalement fonctionnelle

229 Gestion complète des clients - 7 -

230 Accès et manipulation de données Dans les exemples précédents, les données sont stockées dans une collection dynamique Java contenant les comptes clients. Lors de l utilisation de projets d envergure, les bases de données sont utilisées pour réaliser la persistance des données. Les modèles objet relationnel et relationnel objet seront utilisés pour gérer la persistance des données en Java. Transformations objet vers relationnel et relationnel vers objet Les transformations objet relationnel (objets Java vers la base de données) et relationnel objet (base de données vers objets Java) sont appelées mapping. Actuellement, il existe un grand nombre d outils, plus ou moins fonctionnels pour réaliser ces services. Les plus connus étant la librairie OpenSource Hibernate et Java Persistence API (JPA). Sans utiliser un outil de mapping, il est tout à fait possible de réaliser ce service de façon logicielle avec quelques lignes de programmation. Le mécanisme réalisé par le développeur utilise le modèle ou pattern Data Access Object (DAO) largement suffisant dans la plupart des applications professionnelles. Le modèle ou couche de persistance, fournit un ensemble de méthodes pour consulter, ajouter, modifier, supprimer ou lister des objets, images des valeurs de la base de données. 1. Le modèle Data Access Object DAO Ce modèle fournit un ensemble de règles ou consignes à suivre pour la mise en place d un système de persistance à objet. Avec ce modèle de conception, chaque classe utilisée dans le système doit posséder sa propre classe modèle pour gérer sa persistance. Par exemple, nous aurons une classe JavaBean Client.java et une classe ClientModeleDAO.java. La classe modèle contient les méthodes de modification de l état de l objet (ajouter, modifier et supprimer) et de consultation (consulter, lister). Le modèle DAO de base est utilisé par chaque classe d action Struts qui doit réaliser une opération sur la base de données. Le modèle DAO prévoit également la mise en place d une (ou plusieurs) classe mère pour gérer les méthodes communes et la déclaration des connexions au SGBD. L interface DAO est implémentée par la classe DAO principale pour indiquer que la méthode getconnection() doit être surchargée et donc utilisée. La classe ModeleDAO est placée tout en haut de la hiérarchie et implémente la précédente interface DAO. Cette classe fournit l implémentation de la méthode getconnection() qui sera utilisée par les sous classes ainsi que le setter pour le passage de la connexion

231 Diagramme UML du pattern DAO Nous allons utiliser un nouveau projet exemple15 à partir de l exemple précédent avec l utilisation de la partie modèle DAO. La base de données utilisée est appelée struts2. Arborescence du projet exemple15 L interface DAO possède une seule signature de méthode nommée getconnection() pour la connexion au SGBD. Code : exemple15.modeles.dao.java package exemple15.modeles; import java.sql.connection; public interface DAO // Définition de la méthode à déclarer dans les classes utilisatrices public Connection getconnection(); La classe ModeleDAO propose l implémentation de la méthode getconnection() à partir d un pool de connexion (DataSource). Cette méthode retourne une connexion SQL qui peut être utilisée par n importe quelle classe fille du modèle. Les pools de connexions (javax.sql.datasource) permettent de déclarer des connexions à partir du fichier XML de l application (web.xml) et de gérer avec une plus grande souplesse la configuration de celle ci. La connexion à la source de données est alors stockée dans le contexte de l application (ServletContext). Code : exemple15.modeles.modeledao.java package exemple15.modeles; import java.sql.connection; import java.sql.sqlexception; import javax.servlet.servletcontext; import javax.sql.datasource; import org.apache.struts2.servletactioncontext; // Classe de connexion public class ModeleDAO implements DAO - 2 -

232 DataSource datasource=null; // Récupérer une connexion public Connection getconnection() ServletContext servletcontext=servletactioncontext.getservletcontext(); if(this.datasource==null) datasource=(datasource)servletcontext.getattribute("datasource"); Connection connection=null; if(datasource!=null) try connection=datasource.getconnection(); catch(sqlexception e) System.out.println(e); // retourner la connexion return connection; // Positionner une datasource public void setconnection(datasource datasource) this.datasource=datasource; Pour mettre en place la connexion au SGBD, nous allons utiliser un écouteur (listener) lancé au démarrage de l application et configuré dans le fichier de gestion de l application web.xml. Deux balises sont également déclarées pour la connexion au SGBD (<context-param/> et <resource-ref/>). Pour cela, nous pouvons utiliser la classe javax.servlet.servletcontextlistener et les méthodes contextinitialized() et contextdestroyed(). Cette classe est déclarée dans un paquetage spécifique avec une classe statique GestionBaseDeDonnees.java permettant de gérer les fermetures de connexions. Code : /WEB-INF/web.xml <?xml version="1.0" encoding="utf-8"?> <web-app id="webapp_9" version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <!-- Chargeur du datasource --> <listener> <listenerclass>exemple15.boiteoutils.applicationlistener</listener-class> </listener> <!-- Parametre globaux --> <context-param> <param-name>datasourcejndi</param-name> <param-value>java:/comp/env/jdbc_struts2_mysql</paramvalue> </context-param> <filter> <filter-name>struts2</filter-name> <filterclass>org.apache.struts2.dispatcher.ng.filter.strutsprepareandexec utefilter</filter-class> - 3 -

233 </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Informations de connexion a la base de donnees --> <resource-ref> <description>connexion a la base de donnees MySQL</description> <res-ref-name>jdbc_struts2_mysql</res-ref-name> <res-type>javax.sql.datasource</res-type> <res-auth>container</res-auth> </resource-ref> </web-app> Code : exemple15.boiteoutils.applicationlistener.java package exemple15.boiteoutils; import javax.naming.context; import javax.naming.initialcontext; import javax.naming.namingexception; import javax.servlet.servletcontext; import javax.servlet.servletcontextevent; import javax.servlet.servletcontextlistener; import javax.sql.datasource; public class ApplicationListener implements ServletContextListener Context context=null; //fonction appelée lors de la création du lanceur public void contextinitialized(servletcontextevent servletcontextevent) ServletContext servletcontext=servletcontextevent.getservletcontext(); String datasourcejndi=servletcontext.getinitparameter("datasourcejndi"); try context=new InitialContext(); DataSource datasource=(datasource)context.lookup(datasourcejndi); if(datasource==null) System.out.println("Il n y a pas de DataSource pour le projet : exemple15"); else System.out.println("DataSource : exemple15 charge!"); servletcontext.setattribute("datasource", datasource); catch(namingexception e) throw new RuntimeException(); finally try //fermer le context - 4 -

234 initctx!"); if(context!=null) context.close(); catch(exception e) System.out.println("Erreur lors de //fonction appelée lors de la destruction du lanceur public void contextdestroyed(servletcontextevent servletcontextevent) try //fermer le context if(context!=null) context.close(); catch(exception e) System.out.println("Erreur lors de initctx!"); Code : exemple15.boiteoutils.gestionbasededonnees.java package exemple15.boiteoutils; import java.sql.connection; import java.sql.resultset; import java.sql.statement; public class GestionBaseDeDonnees // Permet de fermer un resultset public static void closeresulset(resultset resultat) if(resultat!=null) try resultat.close(); catch(exception e) System.out.println("Erreur lors de la fermeture d une connexion d un resultset"); // Fermeture d une requête public static void closerequest(statement requete) if(requete!=null) try requete.close(); - 5 -

235 catch(exception e) System.out.println("Erreur lors de la fermeture d une requête"); // Fermeture d une connexion public static void closeconnection(connection connexion) if(connexion!=null) try connexion.close(); catch(exception e) System.out.println("Erreur lors de la fermeture d une connexion"); La mise en place du pool de connexion est quasiment terminée, il nous reste la déclaration du pool dans le fichier de configuration du projet (/tomcat/conf/catalina/localhost/exemple15.xml) ou du serveur (/tomcat/conf/catalina/server.xml). Code : /tomcat/conf/catalina/localhost/exemple15.xml <Context path="/exemple15" reloadable="true" docbase="j:\projetweb\exemple15" workdir="j:\projetweb\exemple15\work"> <Resource name="jdbc_struts2_mysql" auth="container" type="javax.sql.datasource" username="root" password="" driverclassname="com.mysql.jdbc.driver" url="jdbc:mysql://localhost:3306/struts2" maxactive="20" maxidle="10" validationquery="select 1" /> </Context> La mise en place d une connexion à un SGBD nécessite l installation du pilote approprié dans le répertoire /Tomcat/lib ou dans le répertoire /WEB INF/lib de l application. Pour le projet, le pilote mysqlconnector java bin.jar est utilisé. Pour gérer la persistance des données nous allons créer la base de données MySQL nommée struts2 avec une table client et trois champs : idclient, identifiant et motdepasse. La table client est complétée avec plusieurs enregistrements pour tester l application. INSERT INTO `client` VALUES (1, jlafosse, jerome ); INSERT INTO `client` VALUES (2, astapane, amelie ); INSERT INTO `client` VALUES (3, amartin, alain ); INSERT INTO `client` VALUES (4, pleroy, pierre ); - 6 -

236 Structure MySQL de la base de données struts2 Nous pouvons commencer par tester notre connexion au SGBD en démarrant l application et en vérifiant que le listener gère correctement le pool. Trace du chargement du pool de connexion dans la console La classe d action Struts n est pratiquement pas modifiée, elle utilise le modèle dynamique ModeleClientDAO en lieu et place du modèle de la classe statique de l exemple précédent. Code : exemple15.clientaction.java package exemple15; import java.util.arraylist; import java.util.list; import com.opensymphony.xwork2.actionsupport; import com.opensymphony.xwork2.modeldriven; import com.opensymphony.xwork2.preparable; import exemple15.javabeans.client; import public class ClientAction extends ActionSupport implements Preparable, ModelDriven private Client client; private List<Client> listeclients; private int idclientencours; public void prepare() throws Exception ModeleClientDAO modeleclientdao=new ModeleClientDAO(); // en création, créer un nouvel objet vide if(idclientencours==0) client=new Client(); // en modification, retourner les infos de l objet else client=modeleclientdao.getclient(idclientencours); public Object getmodel() return client; - 7 -

237 public int getidclientencours() return idclientencours; public void setidclientencours(int idclientencours) this.idclientencours = idclientencours; public Client getclient() return client; public void setclient(client client) this.client = client; public List<Client> getlisteclients() ModeleClientDAO modeleclientdao=new ModeleClientDAO(); listeclients=(arraylist<client>)modeleclientdao.getlisteclient(); return listeclients; public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; // retourner la liste des clients après récupération public String lister() ModeleClientDAO modeleclientdao=new ModeleClientDAO(); listeclients=(arraylist<client>)modeleclientdao.getlisteclient(); return SUCCESS; // ajouter le client dans le modèle public String ajouter() ModeleClientDAO modeleclientdao=new ModeleClientDAO(); modeleclientdao.ajouterclient(client); return SUCCESS; // afficher le formulaire en édition public String editer() return SUCCESS; // modifier un client public String modifier() ModeleClientDAO modeleclientdao=new ModeleClientDAO(); modeleclientdao.modifierclient(client); return SUCCESS; // supprimer un client à partir du paramètre reçu nommé idclient public String supprimer() ModeleClientDAO modeleclientdao=new ModeleClientDAO(); modeleclientdao.supprimerclient(idclientencours); - 8 -

238 return SUCCESS; Enfin, le modèle est basé sur le schéma UML (Unified Modeling Language) précédent et propose les fonctionnalités nécessaire à la consultation, modification, suppression et l ajout des clients ainsi que la méthode de mapping relationnel objet. Les méthodes de la classe correspondent à la définition du du pattern DAO modèle CRUD (Create, Request, Update, Delete) du pattern DAO avec les fonctions permettant de lister les clients, créer un nouveau client (C), récupérer la collection (R), le modifier (U) et enfin le supprimer (D). Code : exemple15.modeles.modeleclientdao.java package exemple15.modeles; import java.sql.connection; import java.sql.preparedstatement; import java.sql.resultset; import java.util.arraylist; import java.util.list; import exemple15.boiteoutils.gestionbasededonnees; import exemple15.javabeans.client; public class ModeleClientDAO extends ModeleDAO // Variables Connection connexion=null; ResultSet resultat=null; private static List<Client> listeclients; // retourner la liste des clients public List<Client> getlisteclient() // Variables PreparedStatement requete=null; Client client=null; String requetestring=null; listeclients=new ArrayList<Client>(); try ORDER BY idclient"; // Ouverture d une connexion connexion=super.getconnection(); // requete pour la liste des clients requetestring="select * FROM client WHERE 1 requete=connexion.preparestatement(requetestring); // Execution de la requête resultat=requete.executequery(); // On stocke le resultat dans une liste if(resultat!=null) while(resultat.next()) // On effectue le mapping des attributs avec les champs de la table SQL client=mapperclient(resultat); des clients // On ajoute l objet a la liste listeclients.add((client)client); catch(exception e) - 9 -

239 System.out.println("Erreur dans la requete dans la classe ModeleClientDAO fonction getlisteclients"); finally try // Fermeture de la connexion if(resultat!=null) GestionBaseDeDonnees.closeResulset(resultat); if(requete!=null) GestionBaseDeDonnees.closeRequest(requete); if(connexion!=null) GestionBaseDeDonnees.closeConnection(connexion); catch(exception ex) System.out.println("Erreur lors de la fermeture de la connexion avec la base de données dans la classe ModeleClientDAO fonction getlisteclients"); // Retourner la liste des clients return listeclients; // trouver un client dans la base public Client getclient(int idclient) // Variables PreparedStatement requete=null; Client client=null; String requetestring=null; try // Ouverture d une connexion connexion=super.getconnection(); idclient=?"; // Création de la requête requetestring = "SELECT * FROM client WHERE // On prépare la requête requete=connexion.preparestatement(requetestring); requete.setint(1,idclient); // Execution de la requête resultat=requete.executequery(); // On stocke le resultat dans l objet client if(resultat!=null) if(resultat.next()) // On effectue le mapping des attributs avec les champs de la table SQL client=mapperclient(resultat);

240 catch(exception e) client=null; System.out.println("Erreur dans la requete dans la classe ModeleClientDAO fonction getclient"); finally try // Fermeture de la connexion if(resultat!=null) GestionBaseDeDonnees.closeResulset(resultat); if(requete!=null) GestionBaseDeDonnees.closeRequest(requete); if(connexion!=null) GestionBaseDeDonnees.closeConnection(connexion); catch(exception ex) System.out.println("Erreur lors de la fermeture de la connexion avec la base de données dans la classe ModeleClientDAO fonction getclient"); // Retourner l objet client return client; // ajouter un client dans la base public int ajouterclient(client client) // Variables PreparedStatement requete=null; String requetestring=null; int codeerreur=0; try // Ouverture d une connexion connexion=super.getconnection(); // Création de la requête requetestring="insert INTO client (identifiant,motdepasse) VALUES(?,?)"; // Préparation de la requête requete=connexion.preparestatement(requetestring); requete.setstring(1, client.getidentifiant()); requete.setstring(2, client.getmotdepasse()); // On vide le client par sécurité client=null;

241 // Execution de la requête codeerreur=requete.executeupdate(); catch(exception e) codeerreur=0; System.out.println("Erreur dans la requete dans la classe ModeleClientDAO fonction ajouterclient"); finally try // Fermeture de la connexion if(resultat!=null) GestionBaseDeDonnees.closeResulset(resultat); if(requete!=null) GestionBaseDeDonnees.closeRequest(requete); if(connexion!=null) GestionBaseDeDonnees.closeConnection(connexion); catch(exception ex) System.out.println("Erreur lors de la fermeture de la connexion avec la base de données dans la classe ModeleClientDAO fonction ajouterclient"); // Retourner le code d erreur return codeerreur; // supprimer un client dans la base public int supprimerclient(int idclient) // Variables PreparedStatement requete=null; String requetestring=null; int codeerreur=0; try // Ouverture d une connexion connexion=super.getconnection(); idclient=?"; // Supprimer le client requetestring="delete FROM client WHERE requete=connexion.preparestatement(requetestring); requete.setint(1, idclient); // Execution de la requête codeerreur=requete.executeupdate(); catch(exception e) codeerreur=0;

242 System.out.println("Erreur dans la requete dans la classe ModeleClientDAO fonction supprimerclient"); finally try // Fermeture de la connexion if(resultat!=null) GestionBaseDeDonnees.closeResulset(resultat); if(requete!=null) GestionBaseDeDonnees.closeRequest(requete); if(connexion!=null) GestionBaseDeDonnees.closeConnection(connexion); catch(exception ex) System.out.println("Erreur lors de la fermeture de la connexion avec la base de données dans la classe ModeleClientDAO fonction supprimerclient"); // Retourner le code d erreur return codeerreur; // modifier un client dans la base public int modifierclient(client client) // Variables PreparedStatement requete=null; String requetestring=null; int codeerreur=0; try // Ouverture d une connexion connexion=super.getconnection(); // Création de la requête requetestring="update client set identifiant=?,motdepasse=? WHERE idclient=?"; requete=connexion.preparestatement(requetestring); requete.setstring(1, client.getidentifiant()); requete.setstring(2, client.getmotdepasse()); requete.setint(3, client.getidclient()); // On vide le client par sécurité client=null; // Execution de la requête codeerreur=requete.executeupdate(); catch(exception e) System.out.println("Erreur dans la requete

243 dans la classe ModeleClientDAO fonction modifierclient"); finally try // Fermeture de la connexion if(resultat!=null) GestionBaseDeDonnees.closeResulset(resultat); if(requete!=null) GestionBaseDeDonnees.closeRequest(requete); if(connexion!=null) GestionBaseDeDonnees.closeConnection(connexion); catch(exception ex) System.out.println("Erreur lors de la fermeture de la connexion avec la base de données dans la classe ModeleClientDAO fonction modifierclient"); // Retourner le code d erreur return codeerreur; // Réaliser le mapping relationnel vers objet public Client mapperclient(resultset resultat) // Variables Client client=new Client(); try if (resultat.getstring("idclient")==null) client.setidclient(0); else client.setidclient(resultat.getint("idclient")); if (resultat.getstring("identifiant")==null) client.setidentifiant(""); else client.setidentifiant(resultat.getstring("identifiant")); if (resultat.getstring("motdepasse")==null) client.setmotdepasse(""); else

244 client.setmotdepasse(resultat.getstring("motdepasse")); catch (Exception e) //Si il y a une erreur durant le mapping des attributs client=null; System.out.println("Erreur lors du mapping des attributs d un client dans la class ModeleClientDAO, fonction mapperclient"); //Retourner l objet client return client;

245 En résumé Ce chapitre a présenté la mise en place de la persistance d objets Java au travers d une base de données relationnelle. Le modèle de persistance DAO a été détaillé au travers de l exemple de gestion des clients. Le projet possède désormais toutes les couches d une application professionnelle Modèle Vue Contrôleur MVC et permet une maintenance et une évolution facilitées par le découpage des différents modules au travers des modèles de conception adaptés

246 Présentation La mise en place du chargement de fichiers du client vers le serveur est souvent une opération contraignante à mettre en place. Struts utilise pour faciliter cette tâche, la librairie commonsfileupload du consortium Jakarta. Sans l utilisation des bibliothèques adaptées, l utilisateur doit utiliser les méthodes de gestion de flux (InputStream et OutputStream). La mise en place du chargement de fichiers nécessite un formulaire HTML avec la balise <form/>. L attribut enctype de cette balise doit être positionné à multipart/form data et la méthode post doit être utilisée en paramètre. Cette déclaration permet d indiquer que le formulaire contient des données multimédia. <form action="monaction" enctype="multipart/form-data" method="post"> La balise <form/> est associée à un champ <input/> de type file. Ce composant HTML permet d intégrer un bouton parcourir afin de choisir le fichier à envoyer sur le serveur. <form action="monaction" enctype="multipart/form-data" method="post"> <input type="file" name="monfichier"/> </form> - 1 -

247 La balise <s:file/> La mise en place de l upload de fichiers avec Struts requiert l utilisation de l intercepteur fileupload. Le chargement de fichiers suit une démarche progressive. Le formulaire indique l action à appeler pour le chargement ainsi que le fichier concerné. L action Struts déclare un objet de type java.io.file utilisé pour faire le lien avec le fichier envoyé. Lors de l utilisation d une liste de fichiers, nous pouvons alors déclarer une collection de fichiers et itérer sur cette liste pour chaque fichier concerné. <s:form action="uploadaction" enctype="multipart/form-data"> <s:file name="monfichier" label="fichier"/> <s:submit/> </s:form> Ensuite, nous devons créer une action avec les propriétés suivantes ainsi que les accesseurs respectifs : private File monfichier; private String monfichierfilename; // syntaxe nomdufichierfilename private String monfichiercontenttype; // syntaxe nomdufichiercontenttype - 1 -

248 L intercepteur fileupload L intercepteur fileupload gère le chargement du ou des fichiers placés dans la requête HTTP. Cet intercepteur possède deux propriétés permettant de gérer la taille maximale du fichier à envoyer et les types autorisés : maximumsize : cette propriété permet de limiter la taille (en octets) du fichier à envoyer. La taille par défaut est de 2 mégaoctets. allowedtypes : cette propriété permet de préciser les types autorisés pour le fichier. Pour notre projet, nous allons gérer l image du client (avatar) avec un système d upload d image web (gif, jpeg et png). Ce projet exemple16 est développé à partir de l application exemple13 permettant de lister et d ajouter les clients. La déclaration de la gestion de l upload est présentée dans le fichier struts.xml. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple16" namespace="/" extends="strutsdefault"> <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple16.clientaction" method="lister"> <result>/jsp/listerclient.jsp</result> </action> <action name="ajouter_client" class="exemple16.clientaction" method="ajouter"> <interceptor-ref name="fileupload"> <param name="maximumsize">102400</param> <param name="allowedtypes">image/gif,image/jpeg,image/png</param> </interceptor-ref> <interceptor-ref name="basicstack"/> <result name="success" type="redirectaction">lister_client</result> </action> </package> </struts> L intercepteur fileupload peut être déclaré seul, mais dans le cas de l utilisation d autres types d action comme dans notre exemple (redirection vers une autre action), l intercepteur basicstack doit être déclaré à la suite de fileupload. L action Ajouter_Client utilise l intercepteur pour la gestion de l upload de fichiers et déclare une taille maximale de fichier de 100 kilooctets ainsi que les images de type gif, jpeg et png autorisées

249 Chargement unique Nous allons réaliser un chargement unique de fichier pour la gestion de l image du client. La classe JavaBean Client est légèrement modifiée pour gérer l image sous la forme d une chaîne de caractères représentant le chemin du répertoire de stockage. Code : exemple16.javabeans.client.java package public class Client private int idclient; private String identifiant; private String motdepasse; private String image; public Client() public Client(int idclient,string identifiant, String motdepasse, String image) this.idclient=idclient; this.identifiant=identifiant; this.motdepasse=motdepasse; this.image=image; // accesseurs Le code de la classe d action est simple, il permet de récupérer automatiquement le fichier, de le copier dans le répertoire des images des clients et d affecter le nom de l image à l objet. Code : exemple16.clientaction.java package exemple16; import java.io.file; import java.util.list; import javax.servlet.servletcontext; import org.apache.struts2.servletactioncontext; import com.opensymphony.xwork2.actionsupport; import exemple16.javabeans.client; import public class ClientAction extends ActionSupport private Client client; private List<Client> listeclients; private File image; private String imagefilename; private String imagecontenttype; public File getimage() return image; public void setimage(file image) this.image = image; public String getimagefilename() return imagefilename; public void setimagefilename(string imagefilename) - 1 -

250 this.imagefilename = imagefilename; public String getimagecontenttype() return imagecontenttype; public void setimagecontenttype(string imagecontenttype) this.imagecontenttype = imagecontenttype; public Client getclient() return client; public void setclient(client client) this.client = client; public List<Client> getlisteclients() listeclients=clientmodele.getlisteclients(); return listeclients; public void setlisteclients(list<client> listeclients) this.listeclients = listeclients; // retourner la liste des clients après récupération public String lister() listeclients=clientmodele.getlisteclients(); return SUCCESS; // ajouter le client dans le modèle public String ajouter() throws Exception // placer le nom du fichier dans le client if(this.imagefilename!=null) client.setimage(this.imagefilename); // copier le fichier dans le répertoire de l application imagesclients ServletContext context=servletactioncontext.getservletcontext(); String repertoireimagesclient=context.getrealpath("imagesclients"); File sauvegardeimage=new File(repertoireImagesClient,this.imageFileName); this.image.renameto(sauvegardeimage); ClientModele.ajouter(this.client); return SUCCESS; Enfin, la vue JSP est également légèrement modifiée pour afficher les images des clients si celles ci ne sont pas nulles. Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> - 2 -

251 <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <!-- Message d erreur lors des actions --> <s:if test="errormessages.size()>0"> <div id="message_erreur"> <label>les erreurs d action suivantes se sont produites : </label> <ul><s:actionerror/></ul> </div> </s:if> <!-- Message de succès --> <s:if test="actionmessages.size()>0"> <div id="message_information"> <ul><s:actionmessage/></ul> </div> </s:if> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="ajouter_client" enctype="multipart/form-data"> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:file name="image" id="image" label="image" labelposition="top" cssclass="input"/> <s:submit value="ajouter un client"/> </s:form> <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot de passe</b></td><td><b>image</b></td></tr> <s:iterator value="listeclients" status="ligne"> <s:if test="#ligne.odd"><tr class="ligne1"></s:if> <s:if test="#ligne.even"><tr class="ligne2"></s:if> <td><s:property value="idclient"/></td> <td><s:property value="identifiant"/></td> <td><s:property value="motdepasse"/></td> <s:if test="image!=null"> <td><img src="imagesclients/$image" width="40" height="40"/></td> </s:if> <s:else> <td> </td> </s:else> </tr> </s:iterator> </table> </div> </body> </html> Nous pouvons désormais ajouter un nouveau client et charger son image depuis le formulaire

252 Upload de l image du client Nous pouvons remarquer que le fichier de validation ClientAction Ajouter_Client validation.xml n est pas géré avec l intercepteur fileupload et que par conséquent, les validations ne sont plus implémentées. Si nous souhaitons ajouter la validation des champs et l utilisation du résultat de type input, nous devons ajouter l intercepteur validationworkflowstack présent dans le fichier struts default.xml et contenant lui même d autres intercepteurs. <interceptor-stack name="validationworkflowstack"> <interceptor-ref name="basicstack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple16" namespace="/" extends="strutsdefault"> <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple16.clientaction" method="lister"> <result>/jsp/listerclient.jsp</result> </action> <action name="ajouter_client" - 4 -

253 class="exemple16.clientaction" method="ajouter"> <interceptor-ref name="fileupload"> <param name="maximumsize">102400</param> <param name="allowedtypes">image/gif,image/jpeg,image/png</param> </interceptor-ref> <interceptor-ref name="validationworkflowstack"/> <result name="success" type="redirectaction">lister_client</result> <result name="input">/jsp/listerclient.jsp</result> </action> </package> </struts> Maintenant que l intercepteur de gestion des validations est opérationnel, nous allons volontairement envoyer un fichier trop lourd (taille supérieure à 100 Ko) et avec un type incorrect (pas une image). Nous observons alors que les erreurs gérées par l intercepteur d upload sont de type action <s:actionerror/>. Les messages affichés sont en anglais et définis dans le fichier struts messages.properties présent dans le paquetage org.apache.struts2 de l archive struts2 core x.jar. Gestion des erreurs sur l upload de fichier Pour surcharger les messages affichés dans les traces d erreurs, nous pouvons créer un fichier nommé strutsmessages.properties placé dans le paquetage de la classe concernée. Code : /WEB-INF/src/struts-message.properties struts.messages.error.uploading=erreur lors de l upload du fichier : 0 struts.messages.error.content.type.not.allowed=erreur. Type de fichier non autorisé struts.messages.error.file.too.large=erreur. La taille du fichier est trop grande Avec un fichier de taille inférieure à 100 Ko mais pas au bon format (ex : un fichier texte), le message renvoyé dans ce cas est le suivant : - 5 -

254 Affichage des messages d erreurs adaptés La vérification du type de contenu est bien effectuée mais les messages d erreurs sont toujours ceux de Struts définis par défaut. Pour que notre fichier struts message.properties soit utilisé, nous devons surcharger le fichier de propriétés Struts en créant un fichier struts.properties dans le répertoire /WEB INF/src de l application et en indiquant avec le paramètre struts.custom.i18n.resources le fichier à utiliser pour les messages. Nous allons en profiter pour définir la taille maximale du fichier à uploader dans ce fichier de configuration. Code : /WEB-INF/src/struts.properties struts.multipart.parser=org.apache.struts2.dispatcher.multipart.ja kartamultipartrequest struts.multipart.maxsize= struts.custom.i18n.resources=struts-messages En cas de gestion du site en plusieurs langues, il serait alors nécessaire de créer autant de fichiers strutsmessage.properties que de langues en les suffixant par la locale (ex : struts message_en.properties)

255 Gestion des messages d erreurs pour la langue - 7 -

256 Chargement multiple L intercepteur fileupload permet de gérer également plusieurs fichiers dans le même formulaire en une seule et même étape. Le principe consiste à créer autant de balises <s:file/> que nécessaire avec des noms identiques. <s:file name="image" id="image" label="image 1" labelposition="top" cssclass="input"/> <s:file name="image" id="image" label="image 2" labelposition="top" cssclass="input"/> Les fichiers sont envoyés à la classe d action sous la forme d un tableau et pourront ainsi être traités en public class ClientAction extends ActionSupport private Client client; private List<Client> listeclients; private File[] image; private String[] imagefilename; private String[] imagecontenttype;... // ajouter le client dans le modèle public String ajouter() throws Exception ServletContext context=servletactioncontext.getservletcontext(); String repertoireimagesclient=context.getrealpath("imagesclients"); for(int i=0;i<image.length;i++) File sauvegardeimage=new File(repertoireImagesClient,this.imageFileName[i]); this.image[i].renameto(sauvegardeimage);... return SUCCESS; - 1 -

257 Chargement en Ajax Le chargement de fichiers étudié précédemment est parfaitement fonctionnel mais il ne permet pas de visualiser l état d avancement du fichier envoyé. Un fichier de grande taille peut demander plusieurs minutes de chargement et ainsi laisser l utilisateur en attente sans information complémentaire. Java propose l utilisation de librairies évoluées permettant le chargement de fichiers en arrière plan avec la technologie Ajax et d utiliser le langage JavaScript pour informer l utilisateur de l état d avancement. Il existe de nombreux plug ins ou librairies Ajax Upload plus ou moins détaillées et fonctionnelles. Nous allons utiliser la librairie Ajax File Upload Plug in (AjaxFileUpload x.jar) du projet Struts 2 input.action La mise en place de ce plug in Struts 2 nécessite l installation des librairies suivantes dans le classpath : ajaxfileupload x.jar : cette librairie permet de gérer l upload de fichiers. commons fileupload x.jar : cette librairie développée par le consortium jakarta, permet de gérer le chargement de fichiers. commons io x.jar : cette librairie permet de gérer les entrées/sorties pour les chargements. json lib x.jar : cette librairie repose sur JSON (JavaScript Object Notation), une librairie JavaScript qui utilise la technologie Ajax pour l accès aux données. Pour mettre en application le chargement de fichiers en mode asynchrone et avec une barre de progression, nous allons créer un nouveau projet exemple17. Ensuite, nous devons ajouter deux balises dans notre vue utilisatrice, (librairie de tags ou taglib) pour la mise en place des librairies nécessaires et le formulaire dynamique. <%@ taglib uri=" prefix="djc"%> //Pour inclure la librairie de tags <djc:head /> //Balise pour inclure automatiquement les fichiers JavaScript Nous devons préciser le nom ajaxfileuploadform dans l id et le paramètre onsubmit à false. Ensuite, le nom du fichier doit être upload. Le bouton de validation du formulaire <s:submit/> doit posséder l attribut onclick avec la méthode Ajax à exécuter et la barre de progression nommée fileuploadprogress. Code : /jsp/imageclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <%@ taglib uri=" prefix="djc" %> <html> <head> <title>ajouter une image</title> <style type="text/css">@import url(css/styles.css);</style> <djc:head /> </head> <body> <div id="enveloppe"> <h3>ajouter une image</h3> <s:form method="post" action="uploader_image" enctype="multipart/form-data" id="ajaxfileuploadform" onsubmit="return false"> <s:file name="upload" id="upload" label="image" labelposition="top" cssclass="input" accept="*/*"/> <s:submit value="envoyer l image" labelposition="top" onclick="return davidjc.ajaxfileupload.initialise(undefined, undefined);"/> </s:form> <div id="fileuploadprogress"> <span id="uploadfilename">initialisation, patientez...</span> <div id="progress-bar"><div id="progressbgrd"> </div></div> <div id="progress-text"> </div> <br/> - 1 -

258 </div> </div> </body> </html> La déclaration de l action doit également être adaptée. L intercepteur employé se nomme fileuploadstacket un résultat de type httpheader est utilisé pour les informations renvoyées en Ajax. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0 //EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple17" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_image" /> <action name="ajouter_image"> <result>/jsp/imageclient.jsp</result> </action> <action name="uploader_image" class="exemple17.clientaction"> <interceptor-ref name="fileuploadstack"> </interceptor-ref> <result name="success" type="httpheader"> <param name="status">200</param> </result> <interceptor-ref name="validationworkflowstack"/> <result name="success" type="redirectaction">ajouter_image</result> </action> </package> </struts> Le code de la classe d action est simple, il possède les différents attributs nécessaires à la gestion du chargement du fichier. Les méthodes getupload(), getuploadcontenttype() et getuploadfilename() sont alors utilisées pour réaliser les actions d upload. Code : exemple17.clientaction.java package exemple17; import java.io.file; import javax.servlet.servletcontext; import org.apache.struts2.servletactioncontext; import com.davidjc.ajaxfileupload.action.fileupload; import public class ClientAction extends FileUpload private File image; private String imagefilename; private String imagecontenttype; // ajouter le client dans le modèle public String execute() this.image=this.getupload(); this.imagecontenttype=this.getuploadcontenttype(); - 2 -

259 this.imagefilename=this.getuploadfilename(); // placer le nom du fichier dans le répertoire if(this.image!=null) // copier le fichier dans le répertoire de l application imagesclients ServletContext context=servletactioncontext.getservletcontext(); String repertoireimagesclient=context.getrealpath("imagesclients"); File sauvegardeimage=new File(repertoireImagesClient,this.imageFileName); this.image.renameto(sauvegardeimage); return Action.SUCCESS; Notre application exemple17 est fonctionnelle nous pouvons la tester avec un fichier de très grande taille pour visualiser le chargement asynchrone et la barre de progression. Arborescence du projet exemple17-3 -

260 Chargement asynchrone de fichiers en Ajax - 4 -

261 En résumé Ce chapitre a présenté la mise en place de la gestion des chargements de fichiers, du client vers le serveur avec le framework Struts. Le premier exemple explique en détail la mise en place de cette technique à partir de l image du formulaire client. Le second paragraphe introduit le chargement multiple et enfin, la dernière partie permet de mettre en œuvre le chargement de fichiers asynchrones avec les technologies JavaScript et Ajax

262 Présentation La mise en place du téléchargement de fichier du serveur vers le client est proposée en standard avec le framework Struts. L envoi dynamique de fichiers est utilisé sur les fiches produit d articles ou pour une gestion de CV des clients par exemple. Le téléchargement de fichiers fonctionne sous la forme de flux et peut donc renvoyer une vidéo ou image directement dans le navigateur du client. Cette technique est également parfois utilisée pour protéger les accès à des contenus confidentiels et fournir alors le flux après authentification ou décryptage. L envoi d un fichier du serveur vers le navigateur client est réalisé en utilisant l attribut Content-Type dans l en tête HTTP, en fonction du type de fichier à envoyer. Le navigateur utilise aussi les attributs Content-Disposition et le paramètre attachment;filename=nomdufichier (ce nom de fichier sera alors utilisé pour sauvegarder le contenu). Point de vue programmation, le fichier est lu du côté serveur par l intermédiaire de la classe FileInputStream et envoyé au navigateur avec une instance de la classe OutputStream. Le code suivant permet de réaliser cette fonction dans une Servlet Java EE : FileInputStream flecture=new FileInputStream(fichier); BufferedInputStream blecture=new BufferedInputStream(flecture); byte[] octets=new byte[blecture.available()]; response.setcontenttype(contenttype); OutputStream sortie=response.getoutputstream(); blecture.read(octets); sortie.write(octets); - 1 -

263 Résultat stream Struts propose d utiliser le résultat de type stream pour le téléchargement de fichiers. Lors de l implémentation du résultat de type stream, l usage d une page JSP pour le résultat n est plus obligatoire, le flux étant envoyé directement au navigateur. Nous allons proposer un nouveau projet exemple18 permettant de télécharger l image du client en l affichant soit directement dans le navigateur sous la forme de flux ou en le téléchargeant directement de manière forcée. Cette différence est réalisée par l intermédiaire du paramètre de l en tête Content Type. Le paramètre Content Type image/png force le navigateur à afficher l image et le paramètre Content Type application/octet stream force le téléchargement de celle ci. Le résultat Struts de type stream possède les paramètres suivants : inputname : ce paramètre permet de préciser la fonction de la classe d action qui va retourner l objet de type InputStream. La classe d action devra donc déclarer le setter associé. buffersize : ce paramètre permet de préciser la taille du buffer/tampon utilisé pour lire les données à envoyer. contenttype : ce paramètre permet de préciser le type de réponse à renvoyer à l en tête du paquet HTTP. contentlength : ce paramètre permet de préciser la taille de la réponse renvoyée. contentdisposition : ce paramètre permet de préciser le nom de l objet qui sera soit affiché dans le navigateur soit enregistré/sauvegardé sur le disque client. Pour notre projet exemple18, nous présentons le fichier struts.xml avec les déclarations des deux actions et les paramètres associés. Les deux actions sont quasiment identiques, elles diffèrent uniquement par le paramètre contenttype permettant pour l action Afficher_Image.action de retourner l image dans le navigateur et pour l action Telecharger_Image.action de forcer le résultat en flux d octets et donc le téléchargement. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple18" namespace="/" extends="strutsdefault"> <default-action-ref name="gerer_image" /> <action name="gerer_image"> <result>/jsp/imageclient.jsp</result> </action> <action name="afficher_image" class="exemple18.telechargeraction"> <result name="success" type="stream"> <param name="inputname">inputstream</param> <param name="contenttype">image/png</param> <param name="contentdisposition">filename="lenomdemonimage.png"</param> <param name="buffersize">2048</param> </result> </action> <action name="telecharger_image" class="exemple18.telechargeraction"> <result name="success" type="stream"> <param name="inputname">inputstream</param> <param name="contenttype">application/octet

264 stream</param> <param name="contentdisposition">filename="lenomdemonimage.png"</param> <param name="buffersize">2048</param> </result> </action> </package> </struts> La classe d action contient le setter pour le chemin du fichier à manipuler et la méthode getinputstream() associée au paramètre inputname permettant de retourner le flux. Code : exemple18.telechargeraction.java package exemple18; import java.io.inputstream; import javax.servlet.servletcontext; import org.apache.struts2.util.servletcontextaware; import public class TelechargerAction extends ActionSupport implements ServletContextAware private String cheminfichier; private ServletContext servletcontext; public void setservletcontext(servletcontext servletcontext) this.servletcontext=servletcontext; public void setcheminfichier(string cheminfichier) this.cheminfichier = cheminfichier; public InputStream getinputstream() throws Exception return servletcontext.getresourceasstream(cheminfichier); La vue JSP permet de réaliser les deux liens adaptés avec en paramètre le chemin du fichier à afficher ou télécharger. La balise <img/> d insertion d image permet d afficher le contenu de l image dynamiquement à partir de l action. Ce principe est très utilisé pour protéger des contenus. Code : /jsp/imageclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>télécharger une image</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>télécharger une image</h3> <a href="afficher_image.action? cheminfichier=/imagesclients/image.png"> Afficher l image </a> <a href="telecharger_image.action? cheminfichier=/imagesclients/image.png"> Télécharger l image </a> <br/> <img src="afficher_image.action? cheminfichier=/imagesclients/image.png" align="bottom"/> </div> - 2 -

265 </body> </html> Arborescence du projet exemple18 Téléchargement d un fichier sous la forme d octets - 3 -

266 Téléchargement dynamique de fichiers L application précédente permet de montrer comment forcer des téléchargements ou retourner des flux d octets directement accessibles par le navigateur. Nous allons développer une nouvelle application exemple19 permettant de télécharger les fichiers sources du projet ou d afficher le code dans le navigateur sous la forme de contenu textuel. Le fichier de configuration de l application struts.xml contient l action Afficher_Fichier.action permettant de télécharger les fichiers sous la forme de document texte (contenttype=text/plain). Cette déclaration est associée à la méthode inputstreamafficher(). La seconde action nommée Telecharger_Afficher.action est associée à la méthode d action inputstreamtelecharger(). Cette méthode, plus complexe que la précédente, permet de préciser dynamiquement le nom du fichier qui sera téléchargé (attribut contentdisposition) en fonction du nom reçu pour bénéficier d un système générique et vérifier que celui ci existe. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple19" namespace="/" extends="strutsdefault"> <default-action-ref name="gerer_fichier" /> <action name="gerer_fichier"> <result>/jsp/fichierclient.jsp</result> </action> <action name="afficher_fichier" class="exemple19.telechargeraction"> <result name="success" type="stream"> <param name="inputname">inputstreamafficher</ param> <param name="contenttype">text/plain</param> </result> </action> <action name="telecharger_fichier" class="exemple19.telechargeraction"> <result name="success" type="stream"> <param name="inputname">inputstreamtelecharger</param> </result> </action> </package> </struts> Code: exemple19.telechargeraction.java package exemple19; import java.io.file; import java.io.fileinputstream; import java.io.inputstream; import javax.servlet.servletcontext; import org.apache.struts2.dispatcher.streamresult; import org.apache.struts2.util.servletcontextaware; import com.opensymphony.xwork2.actionsupport; import com.opensymphony.xwork2.result; import public class TelechargerAction extends ActionSupport implements - 1 -

267 ServletContextAware private String cheminfichier; private String repertoirefichier; private ServletContext servletcontext; public void setservletcontext(servletcontext servletcontext) this.servletcontext=servletcontext; public void setcheminfichier(string cheminfichier) this.cheminfichier = cheminfichier; public void setrepertoirefichier(string repertoirefichier) this.repertoirefichier = repertoirefichier; // Afficher un fichier (contenttype=text/plain) public InputStream getinputstreamafficher() throws Exception return servletcontext.getresourceasstream(cheminfichier); // Telecharger un fichier (contenttype=application/octetstream) public InputStream getinputstreamtelecharger() throws Exception String repertoire=servletcontext.getrealpath(this.repertoirefichier); File fichier=new File(repertoire,this.cheminFichier); if(fichier.exists()) Result result=actioncontext.getcontext().getactioninvocation().getresult( ); if(result!=null && result instanceof StreamResult) StreamResult stream=(streamresult)result; stream.setcontentdisposition("filename="+fichier.getname()); stream.setcontenttype("application/octetstream"); try return new FileInputStream(fichier); catch(exception e) System.out.println(e); return null; Code : /jsp/fichierclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> - 2 -

268 <title>télécharger une image</title> <style url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>télécharger une image</h3> <a href="afficher_fichier.action? cheminfichier=/css/styles.css"> Code source : feuille css </a><br/> <a href="telecharger_fichier.action? repertoirefichier=/css&cheminfichier=styles.css"> Télécharger : feuille css </a><br/> <a href="afficher_fichier.action?cheminfichier=/web- INF/src/exemple19/TelechargerAction.java"> Code source : TelechargerAction.java </a><br/> <a href="telecharger_fichier.action?repertoirefichier=/web- INF/src/exemple19/&cheminFichier=TelechargerAction.java"> Télécharger : TelechargerAction.java </a> </div> </body> </html> Affichage et téléchargement dynamique de fichiers - 3 -

269 - 4 -

270 En résumé Ce chapitre a présenté la mise en place de la gestion des téléchargements de fichiers du serveur vers le client à l aide du framework Struts. Le premier exemple présente la mise en place du résultat streamet la déclaration des téléchargements dans le fichier de configuration struts.xml. Les deux formes de téléchargement sont présentées, à savoir le téléchargement sous la forme de flux pour l affichage dans le navigateur et le téléchargement forcé pour la sauvegarde sur disque dur. Le dernier exemple, plus évolué, permet de gérer les deux types de téléchargement sous forme dynamique et générique à l aide de paramètres et de traitements par la classe d action

271 Présentation Le chargement ou affichage d une page lors d un traitement complexe peut prendre jusqu à plusieurs minutes. La mise en place d un affichage de la progression du chargement est parfois nécessaire mais n est pas une tâche facile à réaliser en programmation Internet. Le framework propose pour cela un intercepteur dédié afin de simuler le chargement et la progression des traitements. L intercepteur execandwait fonctionne sur la base d une session et de paramètres automatiquement mis à jour pour les informations de la progression du traitement. Le principe repose sur un processus ou thread tournant en tâche de fond et retournant des informations à l utilisateur dans l action courante pour l informer et le faire patienter avant que l exécution soit totalement terminée. La mise en place des chargements de pages repose sur les étapes suivantes : Définition de la balise HTML <meta/> permettant de rafraîchir la page. Définition de l action avec l intercepteur execandwait dans le fichier struts.xml. Définition de la méthode d action et de la fonction getcomplete(). La balise <meta/> permet de recharger la page courante (ou une autre page) toutes les n secondes. Une définition courante est un rafraîchissement de la page chaque seconde. <meta http-equiv="refresh" content="1;url=/exemple20/chargement.action"/> L intercepteur execandwait contient trois paramètres pour la gestion de l exécution. Le paramètre threadpriority permet d assigner la priorité au thread. Le paramètre delay précise le nombre de millisecondes à attendre avant que le résultat soit envoyé au client (la valeur par défaut est 0). Enfin, le paramètre delaysleepinterval spécifie le nombre de millisecondes du thread principal vérifiant la fin de l exécution. La méthode getcomplete() est obligatoire lors de la mise en place de l intercepteur execandwait et de la gestion de la progression du chargement, elle permet de retourner un entier représentant la progression du traitement

272 Mise en place Le projet exemple20 permet d afficher la progression du chargement de la page à partir d une action simple réalisant une attente simulant un long traitement. Le résultat est renvoyé vers la page /jsp/chargeur.jsp toutes les secondes étant donné que le paramètre delay est positionné à 1000 millisecondes. L action est rechargée en arrière plan par la balise <meta/>. Le résultat nommé wait permet de préciser la page à afficher pour l attente. Le résultat exécuté en cas de succès est précisé avec l attribut name. Le service d attente est fonctionnel mais affiche une barre d attente sans information sur le temps de traitement, le principe est utilisé pour faire patienter l utilisateur mais en aucun cas pour afficher la progression du traitement effectué. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple20" namespace="/" extends="strutsdefault"> <default-action-ref name="chargeur" /> </package> </struts> <action name="chargeur" class="exemple20.chargeuraction"> <interceptor-ref name="defaultstack"/> <interceptor-ref name="execandwait"> <param name="delay">1000</param> </interceptor-ref> <result name="wait">/jsp/chargeur.jsp</result> <result name="success">/jsp/complet.jsp</result> </action> Code : exemple20.chargeur.action package exemple20; import public class ChargeurAction extends ActionSupport public String execute() System.out.println("Dans l action"); try Thread.sleep(10000); catch(exception e) System.out.println(e); return SUCCESS; Code : /jsp/chargeur.jsp - 1 -

273 taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>chargeur</title> <meta http-equiv="refresh" content="1;url=/exemple20/chargeur.action"/> <style url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>chargeur</h3> <img src="images/chargementcirculaire.gif" align="absmiddle"/> Veuillez patienter pendant le chargement de la page </div> </body> </html> Code : /jsp/complet.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>chargeur Complet</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>chargeur Complet</h3> Le traitement est complet </div> </body> </html> Progression du chargement Le second exemple21 utilise la méthode getcomplete() et la propriété complete dans la classe d action pour afficher la progression du chargement à l utilisateur. La classe d action Chargeur.action ainsi que la vue JSP /jsp/chargeur.jsp sont modifiées pour traiter le paramètre complete. Le paramètre complete est incrémenté de 10 toutes les secondes, sachant que l attente est de 10 secondes, le temps de chargement sera alors correct et précis. Bien entendu, ce chiffre devra être adapté et n est pas très précis mais permet d indiquer la progression à l utilisateur concerné. Code : exemple21.chargeur.action package exemple21; import com.opensymphony.xwork2.actionsupport; - 2 -

274 @SuppressWarnings("serial") public class ChargeurAction extends ActionSupport private int complete=0; public String execute() System.out.println("Dans l action"); try Thread.sleep(10000); catch(exception e) System.out.println(e); return SUCCESS; public int getcomplete() complete+=10; return complete; Code : /jsp/chargeur.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>chargeur</title> <meta http-equiv="refresh" content="1;url=/exemple21/chargeur.action"/> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>chargeur</h3> Veuillez patienter pendant le chargement de la page <s:property value="complete"/>% effectué </div> </body> </html> Chargement de fichier et progression - 3 -

275 En résumé Ce chapitre a présenté la gestion des chargements de pages à l aide de l intercepteur execandwait. Le but de cet intercepteur étant de faire patienter l utilisateur et de fournir des informations sur les traitements réalisés en tâche de fond. Le service fournit en standard permet d appeler une action à intervalles de temps réguliers et de fournir des informations sur la progression grâce à un paramètre dédié

276 Présentation Struts repose sur une liste d intercepteurs chargés de proposer des services spécifiques pour la gestion des paramètres, les validations de formulaires ou encore le chargement de fichiers. Ces intercepteurs utilisent le principe des filtres pour le cycle de vie du processus. La plupart des intercepteurs proposés en standard par Struts sont suffisants pour des applications professionnelles. Cependant, il est parfois nécessaire de développer un intercepteur pour un service spécifique. Un intercepteur n est ni plus ni moins qu une classe Java qui implémente l interface com.opensymphony.xwork2.interceptor. Cette interface propose les mêmes fonctionnalités qu un filtre, à savoir les signatures des méthodes init(), destroy() mais également une nouvelle nommée intercept(actioninvocation invocation). La méthode init() est appelée avant que l intercepteur soit créé pour initialiser des ressources et uniquement lorsque l application est chargée. La méthode intercept() est appelée à chaque fois qu une action est envoyée à l intercepteur pour réaliser le traitement des opérations. La méthode destroy() est appelée après l exécution de l intercepteur pour libérer des ressources et uniquement lorsque l application est déchargée. Le framework appelle ainsi la méthode intercept() de chaque intercepteur déclaré pour l action. L objet instance de la classe ActionInvocation représente l état de l action et permet à celle ci de récupérer les objets Action et Result associés. La classe AbstractInterceptor implémente l interface Interceptor et fournit une implémentation standard des méthodes init() et destroy(). Fonctionnement des filtres Java - 1 -

277 Écrire un intercepteur Pour mettre en application le développement de la création d un intercepteur, nous allons créer un outil de gestion d authentification client, pour l accès aux actions utilisatrices. Les intercepteurs personnels sont principalement utilisés pour l authentification, la connexion au SGBD (par l intermédiaire d un pool JNDI) ou encore le cryptage/décryptage de données. Le projet exemple22 utilise la classe exemple22.authentificationintercepteur.java comme intercepteur chargé de gérer l authentification pour l accès à l action Proteger.action. Le fichier de configuration struts.xml contient la définition de l intercepteur avec la balise <interceptors/> et la référence à la classe exemple22.authentificationintercepteur.java. Quatre actions sont déclarées, l action Initialiser.action permet d afficher la page JSP pour lister les ressources. L action Proteger.action utilise l intercepteur déclaré par l intermédiaire de la balise <interceptor-ref/> et propose deux résultats. Enfin, deux actions sont dédiées à la gestion de l authentification, l étape de connexion avec la mise en place de deux paramètres pour les informations de connexion (identifiantdefaut et motdepassedefaut) et l étape de déconnexion. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple21" namespace="/" extends="strutsdefault"> <interceptors> <interceptor name="authentificationintercepteur" class="exemple22.authentificationintercepteur"> <param name="identifiantdefaut">jlafosse</param> <param name="motdepassedefaut">jerome</param> </interceptor> </interceptors> <default-action-ref name="initialiser" /> <action name="initialiser"> <result>/jsp/liste.jsp</result> </action> <action name="proteger"> <interceptor-ref name="createsession"/> <interceptor-ref name="defaultstack"/> <interceptor-ref name="authentificationintercepteur"/> <result name="authentification">/jsp/authentification.jsp</result> <result>/jsp/ressource.jsp</result> </action> <action name="connecter" class="exemple22.authentifieraction" method="connecter"> <param name="identifiantdefaut">jlafosse</param> <param name="motdepassedefaut">jerome</param> <result name="input">/jsp/authentification.jsp</result> <result type="redirectaction">proteger</result> </action> <action name="deconnecter" class="exemple22.authentifieraction" method="deconnecter"> <result type="redirectaction">initialiser</result> - 1 -

278 </action> </package> </struts> La classe AuthentifierAction.java permet de vérifier l authentification des clients en fonction des paramètres par défaut, déclarés dans le fichier de configuration de l application struts.xml. En cas de succès, un paramètre nommé authentification est sauvegardé en session, dans le cas contraire, le formulaire d authentification est affiché à nouveau. Code : exemple22.authentifieraction.java package exemple22; import java.util.map; import org.apache.struts2.interceptor.sessionaware; import public class AuthentifierAction extends ActionSupport implements SessionAware private String identifiant; private String motdepasse; private String identifiantdefaut; private String motdepassedefaut; private Map<String,Object> sessionmap; public void setsession(map<string,object> map) this.sessionmap=map; // accesseurs public String connecter() // vérifier si l identifiant et le mot de passe sont corrects if(identifiant!=null && motdepasse!=null) if(identifiant.equals(identifiantdefaut) && motdepasse.equals(motdepassedefaut)) // authentification correcte, sauvegarder la valeur dans la session this.sessionmap.put("authentification", true); return SUCCESS; return INPUT; public String deconnecter() // vider la session de l utilisateur this.sessionmap.clear(); return SUCCESS; La classe de l intercepteur AuthentificationIntercepteur.java utilise les trois méthodes courantes et les paramètres de la session pour vérifier si l utilisateur peut accéder à l action et aux pages JSP concernées. Les développeurs pourront ainsi mettre en place le système d authentification pour la totalité d un service en utilisant le code suivant : <action name="action">... <interceptor-ref name="authentificationintercepteur"/>

279 </action> Code : exemple22.authentificationintercepteur.java package exemple22; import java.util.map; import com.opensymphony.xwork2.actioninvocation; import public class AuthentificationIntercepteur extends AbstractInterceptor // méthode exécutée avant l action public void init() System.out.println("Avant la méthode d action"); // méthode de l intercepteur public String intercept(actioninvocation invocation) throws Exception System.out.println("Dans la méthode intercept"); // récupérer les objets de la session Map<String,Object> session=invocation.getinvocationcontext().getsession(); if(session.get("authentification")==null) // l utilisateur n est pas authentifié retourner sur la page d authentification return "authentification"; else // vérifier l authentification boolean authentification=(boolean) (session.get("authentification")); if(authentification) // l utilisateur est authentifié, continuer l action return invocation.invoke(); else // l utilisateur n est pas authentifié retourner sur la page d authentification return "authentification"; // méthode exécutée après l action public void destroy() System.out.println("Après la méthode d action"); Les pages JSP permettent d afficher respectivement un lien vers les ressources protégées par authentification (/jsp/liste.jsp), un formulaire d authentification (/jsp/authentification.jsp) et les ressources protégées elles même (/jsp/ressource.jsp). Code : /jsp/liste.jsp <%@ taglib prefix="s" uri="/struts-tags" %> - 3 -

280 <html> <head> <title>liste</title> <style url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>liste</h3> <a href="proteger.action">accéder à la ressource</a> </div> </body> </html> Code : /jsp/authentification.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>authentification</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>authentification</h3> <s:form method="post" action="connecter"> <s:textfield name="identifiant" id="identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="motdepasse" id="motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="valider"/> </s:form> </div> </body> </html> Code : /jsp/ressource.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ressource</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ressource</h3> Cette ressource est protégée, vous êtes authentifié <br/><a href="deconnecter.action">se deconnecter</a> </div> </body> </html> - 4 -

281 Formulaire d authentification client Accès protégé par intercepteur Struts personnalisé - 5 -

282 En résumé Ce chapitre a présenté l écriture personnelle d intercepteurs afin d améliorer les services proposés par Struts par l intermédiaire de l implémentation de l interface Interceptor ou en héritant de la classe abstraite AbstractInterceptor. La programmation d intercepteurs permet ainsi de simplifier les classes, les structures et le partage de services

283 Présentation Struts propose en standard différents types de résultats utilisés tout au long de l ouvrage comme Dispatcher, Stream ou encore redirectaction. Le framework propose également de créer des résultats personnalisés en fonction d un besoin. Un résultat Struts doit implémenter l interface com.opensymphony.xwork2.result. Cette interface possède une seule méthode nommée execute(actioninvocation invocation), déclenchée lorsque le résultat est exécuté ou doexecute (String finallocation, ActionInvocation invocation) qui permet de préciser la destination finale de l action pour le routage. Le but de la création de résultats personnels étant d écrire le code qui va être exécuté lorsque le résultat sera renvoyé. Le principe est quasiment identique au développement d intercepteur et consiste à hériter de la classe org.apache.struts2.dispatcher.strutsresultsupport qui elle même implémente l interface Result

284 Écrire un résultat Le projet exemple23 basé sur l application exemple14 (gestion des comptes clients sous forme d une collection) propose un résultat permettant de retourner la liste des comptes utilisateur sous la forme de flux RSS plutôt que textuelle. RSS (Really Simple Syndication) est un format d échange de données au format XML. Les sources sont alors distribuables, réutilisables et désignées par flux RSS ou syndication. Les navigateurs récents peuvent lire directement les fichiers RSS mais nous pouvons également utiliser un logiciel spécialisé appelé lecteur RSS. Un document RSS est un fichier au format XML contenant la balise <rss/>, et au moins un canal avec la balise <channel/> qui fournit des informations sur le site. Ce canal est composé d un certain nombre d articles et de balises <item/> qui représentent les informations. Le fichier struts.xml contient la définition du résultat avec la balise <result-types/> et l association de la classe : <result-types> <result-type name="rss" class="exemple23.rssresult"/> </result-types> Le fichier struts.xml définit également une action nommée ListerRss_Client.action contenant un résultat de type rss pour le traitement des informations. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple23" namespace="/" extends="strutsdefault"> <result-types> <result-type name="rss" class="exemple23.rssresult"/> </result-types> <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple23.clientaction" method="lister"> <result>/jsp/listerclient.jsp</result> </action> <action name="listerrss_client" class="exemple23.clientaction" method="lister"> <result type="rss">/jsp/listerclient.jsp</result> </action> <action name="ajouter_client" class="exemple23.clientaction" method="ajouter"> <result name="input">/jsp/listerclient.jsp</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="editer_client" class="exemple23.clientaction" method="editer"> <interceptor-ref name="paramsprepareparamsstack"/> <result name="success">/jsp/editerclient.jsp</result> </action> <action name="modifier_client" class="exemple23.clientaction" method="modifier"> <result name="input">/jsp/editerclient.jsp</result> <result name="success" - 1 -

285 type="redirectaction">lister_client</result> </action> <action name="supprimer_client" class="exemple23.clientaction" method="supprimer"> <result name="success" type="redirectaction">lister_client</result> </action> </package> </struts> Le code de l intercepteur permet de récupérer l instance de la réponse HTTP et de définir un résultat sous la forme de contenu XML (setcontenttype). La liste dynamique des clients, présente dans l objet invocation et retournée par la classe d action, est récupérée et mise en forme pour respecter l affichage RSS (canal et articles). Code : exemple23.rssresult.java package exemple23; import java.io.printwriter; import java.util.list; import javax.servlet.http.httpservletresponse; import org.apache.struts2.strutsstatics; import org.apache.struts2.dispatcher.strutsresultsupport; import com.opensymphony.xwork2.actioninvocation; import public class rssresult extends StrutsResultSupport // méthode exécutée par le résultat public void doexecute(string finallocation,actioninvocation invocation) throws Exception // récuperer l objet response HttpServletResponse response=(httpservletresponse)invocation.getinvocationcontext().get (StrutsStatics.HTTP_RESPONSE); // récupérer la liste des clients dans la pile d exécution List<Client> listeclients=(list<client>)invocation.getstack().findvalue("liste Clients"); 8\"?>"); // type de réponse response.setcontenttype("application/xml"); PrintWriter out=response.getwriter(); out.println("<?xml version=\"1.0\" encoding=\"utf- out.println("<rss version=\"2.0\">"); // créer un canal out.println("<channel>"); out.println("<title>flux RSS des clients</title>"); out.println("<link> out.println("<description>flux RSS des clients</description>"); // créer les articles clients for(int i=0;i<listeclients.size();i++) Client client=(client)listeclients.get(i); out.println("<item>"); out.println("<title> Client : "+client.getidentifiant()+"</title>"); out.println("<link> action?idclientencours="+client.getidclient()+"</link>"); out.println("<description> Client : "+client.getidclient()+" - "+client.getidentifiant()+"

286 "+client.getmotdepasse()+"</description>"); out.println("</item>"); out.flush(); out.close(); Un lien vers l action ListerRss_Client.action est ajouté dans la vue JSP /jsp/listerclient.jsp, responsable de l affichage des informations. <a href="listerrss_client.action">afficher la liste au format RSS</a> Gestion des clients au format RSS - 3 -

287 Affichage du résultat au format RSS - 4 -

288 En résumé Ce chapitre a expliqué en détail l écriture de résultats personnalisés qui seront utilisés dans les actions Struts. L exemple permet ainsi de manipuler les données envoyées, de les modifier et de retourner le résultat sous différents formats. Cette technique est utilisée dans les résultats proposés par Struts pour réaliser des redirections, des affichages XML ou encore pour retourner des flux d octets

289 Présentation Lors de la navigation entre les différentes pages d une application Internet, il arrive souvent qu une action soit appelée deux fois de suite ou qu un ajout d article dans le panier soit doublé par erreur. Ce double clic ou double envoi devient plus contraignant lorsque cela correspond à l étape de paiement (somme retirée deux fois sur le compte client) ou d insertion de données (création de deux articles au lieu d un seul). Cette double validation survient lorsque le traitement côté serveur est long et que l utilisateur clique plusieurs fois sur le bouton d envoi ou lorsque l utilisateur rafraîchit la page courante réalisant l action. Struts propose une technique de gestion du double envoi pouvant être appliquée à d autres langages. La technique consiste à utiliser un jeton unique pour chaque utilisateur, sauvegardé sur le serveur et possédant une copie insérée dans chaque formulaire HTML. Lorsque le formulaire est envoyé au serveur, l application compare le jeton envoyé avec celui présent sur le serveur, il réalise l action en cas d égalité et supprime ensuite le jeton du serveur. Ainsi, lors d un double envoi par erreur ou involontaire, le jeton sur le serveur étant supprimé, l action n est donc pas exécutée. Struts propose la balise <s:token/> pour gérer le jeton côté client. Cette balise doit être placée dans une balise <s:form/> et génère un champ caché de type <hidden/> pour la sauvegarde du jeton. La mise en place d un jeton dans une action nécessite la déclaration de l intercepteur Token ou TokenSession. L intercepteur Token retourne un résultat nommé invalid.token ajouté dans la liste des erreurs d action <s:actionerror/>. Pour adapter le message dans la langue voulue, nous pouvons créer la variable struts.messages.invalid.token dans le fichier de la langue concernée

290 Mise en place du jeton Pour mettre en place la gestion du jeton et de l intercepteur TokenInterceptor, nous allons utiliser l exemple de gestion des clients exemple14 dans un nouveau projet exemple24. La méthode ajouter() permet de créer un nouveau client et va être modifiée pour montrer le problème du double envoi. Pour cela, nous allons créer un thread d attente de plusieurs secondes. Code : exemple24.clientaction.java package exemple24; import java.util.list; import com.opensymphony.xwork2.actionsupport; import com.opensymphony.xwork2.modeldriven; import com.opensymphony.xwork2.preparable; import exemple24.javabeans.client; import public class ClientAction extends ActionSupport implements Preparable, ModelDriven... // ajouter le client dans le modèle public String ajouter() try Thread.sleep(4000); catch(exception e) ClientModele.ajouter(client); return SUCCESS;... Le thread d attente permet de simuler un long chargement de la page lors d un ajout de client. Nous pouvons alors afficher le projet et créer un nouveau compte. Le temps de chargement de la page étant long, nous pouvons cliquer sur le bouton d ajout (Ajouter un client) plusieurs fois de suite. Nous remarquons alors les insertions multiples dans la liste

291 Formulaire d ajout client Affichage multiple des comptes clients Pour éviter ce double envoi, nous allons utiliser l intercepteur token et le nom invalid.token dans le résultat afin de gérer l affichage du jeton dans la définition de l action Ajouter_Client.action. Nous utilisons également l intercepteur validationworkflowstack pour conserver la mise en place des validations de formulaire. <action name="ajouter_client" class="exemple24.clientaction" method="ajouter"> - 2 -

292 <interceptor-ref name="token"/> <interceptor-ref name="validationworkflowstack"/> <result name="invalid.token">/jsp/listerclient.jsp</result> <result name="input">/jsp/listerclient.jsp</result> <result name="success" type="redirectaction">lister_client</result> </action> L erreur du jeton sera affichée par l intermédiaire de la balise <s:actionerror/>. Pour gérer le jeton de l intercepteur, nous ajoutons la balise <s:token/> dans le formulaire d ajout /jsp/listerclient.jsp. Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <!-- Message d erreur lors des actions --> <s:if test="errormessages.size()>0"> <div id="message_erreur"> <label>les erreurs d action suivantes se sont produites : </label> <ul><s:actionerror/></ul> </div> </s:if> <!-- Message de succès --> <s:if test="actionmessages.size()>0"> <div id="message_information"> <ul><s:actionmessage/></ul> </div> </s:if> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="ajouter_client"> <s:token/>... </div> </body> </html> Le code source généré par la balise <s:token/> dans le formulaire HTML est le suivant par exemple : <input type="hidden" name="struts.token" value="lqv6rkhkoletxkrzsr32b5vpgxiwtja4" /> Nous pouvons tester à nouveau l application en réalisant plusieurs validations du formulaire à la suite. Le message d erreur lié au jeton est alors affiché et une seule création est réalisée

293 Gestion du double clic avec le message par défaut Le message d erreur associé à la gestion des jetons est la clé struts.messages.invalid.token présente dans le fichier struts messages.properties livré en standard avec Struts. La classe de gestion de l intercepteur est org.apache.struts2.interceptor.tokeninterceptor. Pour surcharger le message affiché dans l erreur d action il est nécessaire de créer un fichier nommé TokenInterceptor.properties présent dans l arborescence /WEB INF/classes/org/apache/struts2/interceptor. Une seconde technique consiste à créer, comme dans les exemples précédents du livre, un fichier struts.properties dans le répertoire /WEB INF de l application avec le paramètre struts.custom.i18n.resources et un second nommé strutsmessages.properties avec la clé et sa valeur. Code : struts.properties struts.custom.i18n.resources=struts-messages Code : struts-messages.properties struts.messages.invalid.token=vous avez envoyé le formulaire deux fois de suite. Un seul envoi a été validé

294 Formulaire client avec gestion du double clic multilingue Arborescence du projet exemple24 L intercepteur tokensession est similaire à l intercepteur token, la différence réside dans la façon de sauvegarder les données lors de la validation du formulaire. L intercepteur tokensession sauvegarde les données du formulaire dans la session utilisateur

295 En résumé Ce chapitre a expliqué en détail comment éviter les erreurs liées au double clic ou au double envoi lorsque l utilisateur clique plusieurs fois sur le bouton de validation ou rafraîchit la page courante. La technique utilisée pour éviter le double envoi consiste à utiliser un jeton ou token, similaire du côté client et serveur et valable pour un seul échange. Pour mettre en place cette technique, Struts propose l utilisation de la balise <s:token/> dans les vues et les intercepteurs token et tokensession

296 Présentation Les premières versions de Struts 2 étaient livrées en standard avec le plug in Dojo qui est un framework JavaScript OpenSource proposant un ensemble de balises pour gérer des composants Ajax. Cette librairie proposait par exemple la balise <sx:head/> pour insérer automatiquement les librairies JavaScript, la balise <sx:div/> pour gérer les appels Ajax ou encore la balise <sx:submit/> pour poster des formulaires en utilisant la technologie Ajax (sans recharger la page). Malheureusement, la version de Dojo présente dans le plug in est une ancienne version et il n est pas possible de mettre à jour cette librairie. De même, cette librairie basée sur la version 0.4 de Dojo est particulièrement lente. Les concepteurs de Struts précisent d ailleurs que l utilisation du plug in Dojo pour Struts supérieur à 2.1 est dépréciée et que la maintenance n est plus supportée pour cette librairie. Les développeurs Struts travaillent actuellement sur un plug in basé sur la librairie JavaScript JQuery, mais précisent que les développeurs peuvent utiliser n importe quel framework JavaScript avec Struts. Actuellement, les protagonistes d Internet parlent beaucoup de technologies Web 2.0. Derrière ce terme se cache un ensemble de technologies et d outils axés sur l ergonomie des interfaces. L expression a été proposée pour désigner les nouvelles technologies et le renouveau d Internet. Le terme a été inventé par un membre de la société O Reilly pour désigner la renaissance du web. Le but étant d utiliser les technologies d Internet tout en se rapprochant des interfaces homme/machine attractives des logiciels installés sur les machines personnelles. La définition exacte du Web 2.0 n est toujours pas très claire, cependant il est admis qu un site Web 2.0 possède les caractéristiques suivantes : La gestion des informations du site est facilitée (lister, consulter, modifier et supprimer). Le site est totalement utilisable avec un navigateur standard. Le site utilise des standards de technologie. Du point de vue technologique, l infrastructure Web 2.0 est assez complexe, elle inclut à la fois le ou les serveurs, la syndication de contenu, la messagerie et les applications

297 Installation du framework JavaScript De nombreux framework spécialisés en JavaScript sont développés spécifiquement depuis plusieurs années et permettent d apporter un ensemble de services Web 2.0. Nous pouvons citer par exemple Prototype ( ainsi que Scriptaculous ( basé sur Prototype et qui apporte de nouvelles fonctionnalités Ajax et DHTML. La librairie la plus puissante mais aussi la plus lourde et complexe à utiliser est actuellement sans aucun doute ExtJs ( Enfin, la plus utilisée et préconisée par les concepteurs Struts est JQuery ( Cette librairie très légère dans une version compressée (20 Ko environ) permet de manipuler les documents HTML et XHTML. De même, elle fournit un système simple d accès aux balises des documents par notation pointée. Ce framework propose plusieurs centaines de plug ins pour gérer l affichage, les formulaires, Ajax, les validations ou encore les utilisations de la souris. La mise en place de ce framework est très simple et repose sur trois étapes : Téléchargement de la librairie JavaScript et de façon optionnelle des plug ins nécessaires. Installation des fichiers.js téléchargés dans un répertoire de l application (exemple : /jscripts). Déclaration des inclusions de librairies dans les pages JSP utilisatrices (exemple : <script src="jscripts/jquery.min.js" type="text/javascript"></script>)

298 Technologie Ajax AJAX (Asynchronous JavaScript And XML) est une technique de développement Internet basée sur le langage JavaScript permettant d effectuer des requêtes HTTP sans recharger les pages. Cette technologie rend plus interactifs les sites et propose aux utilisateurs une ergonomie proche des logiciels. Ajax repose sur l utilisation des technologies HTML et CSS ainsi que DOM (Document Object Model) pour la représentation des objets, et enfin l objet JavaScript XMLHTTPRequest, pour la réalisation des requêtes en arrière plan. La technologie Ajax oblige cependant à repenser les développements d applications. En effet, les services sont appelés en arrière plan et sont retournés sous la forme d une variable. Dans le cas de notre formulaire d ajout d un client à partir de ses identifiants et mots de passe, nous devons modifier la réponse car le formulaire ne sera pas rechargé. Seules les réponses d erreurs et succès devront être renvoyées et traitées. Gestion du formulaire d ajout client en Ajax Nous allons, pour réaliser l exemple, utiliser le framework JQuery ainsi qu un ensemble de plug ins pour effectuer les principales opérations utiles d une application Ajax Web 2.0. Pour cela, nous commençons avec un nouveau projet exemple25 permettant de gérer les clients et la librairie JQueryForm Plug in ( pour envoyer le formulaire en Ajax sans recharger la page. La mise en place est modifiée du point de vue de la structure. En effet, les réponses aux erreurs de validation <result name="input"> et de succès <result name="success"> doivent toujours retourner sur la même page d affichage des messages (erreurs, erreurs d action et succès). Les autres affichages sont gérés en Ajax afin de ne pas recharger les pages. Le fichier de gestion des actions struts.xml possède les déclarations pour le listing, l ajout, la modification et la suppression. Ces actions sont réalisées en arrière plan et retournent uniquement le message adapté en fonction de l action (validation des champs, erreur, succès). L action Formulaire_Client.action permet de retourner sur le fichier de gestion du client en ajout ou modification suivant la présence ou non de l objet client. Cette action utilise l intercepteur paramsprepareparamsstack pour gérer le passage de paramètres. Enfin, la dernière action Index_Client.action permet d afficher la page principale de gestion. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple25" namespace="/" extends="strutsdefault"> <default-action-ref name="index_client" /> <action name="index_client" class="exemple25.clientaction"> <result>/jsp/indexclient.jsp</result> </action> <action name="formulaire_client" class="exemple25.clientaction" method="editer"> <interceptor-ref name="paramsprepareparamsstack"/> <result>/jsp/formulaireclient.jsp</result> </action> <action name="lister_client" - 1 -

299 class="exemple25.clientaction" method="lister"> <result>/jsp/listerclient.jsp</result> </action> <action name="ajouter_client" class="exemple25.clientaction" method="ajouter"> <result name="input">/jsp/reponseajax.jsp</result> <result name="success">/jsp/reponseajax.jsp</result> </action> <action name="modifier_client" class="exemple25.clientaction" method="modifier"> <result name="input">/jsp/reponseajax.jsp</result> <result name="success">/jsp/reponseajax.jsp</result> </action> <action name="supprimer_client" class="exemple25.clientaction" method="supprimer"> <result name="input">/jsp/reponseajax.jsp</result> <result name="success">/jsp/reponseajax.jsp</result> </action> </package> </struts> La vue /jspindexclient.jsp contient la déclaration des fichiers JavaScript pour le framewok JQuery et son plug in de gestion des formulaires. Cette page très simple contient deux blocs utilisés pour afficher le contenu du formulaire et la liste des clients. Code : /jsp/indexclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> <!-- Utiliser la librairie JavaScript JQuery --> <script src="javascript/jquery.min.js" type="text/javascript"></script> <script src="javascript/jquery.form.js" type="text/javascript"></script> <script src="javascript/exemplejform.js" type="text/javascript">< /script> </head> <body> <!-- balise utilisée pour afficher les réponses Ajax --> <div id="boitereponseajax"></div> <div id="enveloppe"> <!-- balise utilisée pour ajouter/modifier un client en Ajax --> <div id="boiteformulaireclient"></div> <!-- balise utilisée pour afficher la liste des clients en Ajax --> <div id="boitelisterclient"></div> </div> </body> </html> La page /jsp/formulaireclient.jsp est très simple, elle permet d afficher le formulaire client en création ou modification, suivant le paramètre reçu. Code : /jsp/formulaireclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <!-- formulaire en ajout ou modification --> <s:if test="client.idclient!=0"> <h3>editer un client</h3> <s:set id="action">modifier_client.action</s:set> </s:if> - 2 -

300 <s:else> <h3>ajouter un client</h3> <s:set id="action">ajouter_client.action</s:set> </s:else> <s:form method="post" action="%action" id="formulaire_client" name="formulaire_client"> <s:hidden key="client.idclient"/> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:submit value="valider"/> </s:form> La page /jsp/listerclient.jsp reprend le code d affichage des données client. Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot de passe</b></td><td colspan="2" align="center"><b>gestion</b></td></tr> <s:iterator value="listeclients" status="ligne"> <s:if test="#ligne.odd"><tr class="ligne1"></s:if> <s:if test="#ligne.even"><tr class="ligne2"></s:if> <td><s:property value="idclient"/></td> <td><s:property value="identifiant"/></td> <td><s:property value="motdepasse"/></td> <td align="center"><a href="javascript:modifierclient( $ idclient )"><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="javascript:supprimerclient( $ idclient )"><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> </s:iterator> </table> Enfin, la page /jsp/reponseajax.jsp utilisée par les actions permet d afficher simplement les messages reçus en Ajax. Code : /jsp/reponseajax.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <!-- Message d erreur --> <s:if test="errors.size()>0"> <div id="message_erreur"> <label>les erreurs suivantes se sont produites : </label> <ul><s:fielderror/></ul> </div> </s:if> <!-- Message d erreur lors des actions --> <s:if test="errormessages.size()>0"> <div id="message_erreur"> <label>les erreurs d action suivantes se sont produites : </label> <ul><s:actionerror/></ul> </div> </s:if> <!-- Message de succès --> <s:if test="actionmessages.size()>0"> <div id="message_information"> <ul><s:actionmessage/></ul> </div> </s:if> Le traitement de l application est maintenant géré dans un fichier JavaScript utilisé en complément du framewok JQuery. Ce fichier JavaScript nommé exemplejform.js utilise la méthode $(document).ready(function()) permettant de réaliser des actions lorsque la page est chargée. Nous utilisons cette méthode afin de charger le formulaire d ajout ou modification du client et de déclencher l action permettant de lister les enregistrements

301 Ensuite, ce fichier contient quatre méthodes correspondant aux actions à réaliser : ajouterclient() : cette méthode permet d appeler de façon asynchrone l action Formulaire_Client.action et de préciser que le formulaire utilisera le plug in ajaxform pour l envoi de ces informations. listerclient() : cette méthode permet d appeler de façon asynchrone l action Lister_Client.action et d afficher le contenu dans la boîte BoiteListerClient présente dans la page courante. supprimerclient(idclientencours) : cette méthode permet d appeler de façon asynchrone l action Supprimer_Client.action et de passer le client concerné. Lorsque le résultat est exécuté, la méthode de listing est à nouveau déclenchée pour actualiser la liste. modifierclient(idclientencours) : cette méthode permet d appeler de façon asynchrone l action Formulaire_Client.action et d afficher le formulaire en modification en assignant le plug in ajaxform afin de rendre le formulaire asynchrone. Lors de la modification, le listing des clients est actualisé et le formulaire d ajout est réaffiché en remplacement. Code : /javascript/exemplejsform.js // action effectuées au démarrage $(document).ready(function() ); // retourner la liste des clients listerclient(); // retourner le formulaire d ajout des clients ajouterclient(); // retourner le formulaire des clients en utilisant Ajax function ajouterclient() //envoyer les donnees en POST $.ajax( type: "POST", url: "Formulaire_Client.action", datatype: "html", timeout : 8000, error: function() alert( Deconnexion du serveur );, beforesend : function() $("#BoiteFormulaireClient").html( <img src="images/chargementcirculairepetit.gif" align="absmiddle" width="16" height="16" style="padding-right:5px;"/>patientez... );, success: function(html) // mettre le résultat reçu dans la balise de la page courante $("#BoiteFormulaireClient").html(html); // utiliser le formulaire ajaxform $( #Formulaire_Client ).ajaxform( // préciser la cible qui va recevoir la réponse (balise) target: #BoiteReponseAjax, // fonction déclenchée à la suite de l envoi du formulaire success: function() // afficher les réponses (erreurs, erreurs d action, succès) $( #BoiteReponseAjax ).fadein( slow ); // ajout réalisé avec succès if($ - 4 -

302 ( #BoiteReponseAjax ).html().indexof("message_information")!=-1) // récupérer la nouvelle liste de client listerclient(); // vider tous les champs du formulaire $ ( #Formulaire_Client ).resetform(); $ ( #Formulaire_Client ).clearform(); ); ); // retourner la liste des clients en utilisant Ajax function listerclient() //envoyer les donnees en POST $.ajax( type: "POST", url: "Lister_Client.action", datatype: "html", timeout : 8000, error: function() alert( Deconnexion du serveur );, beforesend : function() $("#BoiteListerClient").html( <img src="images/chargementcirculairepetit.gif" align="absmiddle" width="16" height="16" style="padding-right:5px;"/>patientez... );, success: function(html) // mettre le résultat reçu dans la balise de la page courante $("#BoiteListerClient").html(html); ); // supprimer le client function supprimerclient(idclientencours) //envoyer les donnees en POST $.ajax( type: "POST", url: "Supprimer_Client.action", datatype: "html", data: "idclientencours="+idclientencours, timeout : 8000, error: function() alert( Deconnexion du serveur );, beforesend : function() $("#BoiteListerClient").html( <img src="images/chargementcirculairepetit.gif" align="absmiddle" width="16" height="16" style="padding-right:5px;"/>patientez... );, success: function(html) // afficher la réponse ajax $( #BoiteReponseAjax ).html(html); // suppression réalisée avec succès - 5 -

303 if($ ( #BoiteReponseAjax ).html().indexof("message_information")!=-1) listerclient(); ); // modifier le client function modifierclient(idclientencours) //envoyer les donnees en POST $.ajax( type: "POST", url: "Formulaire_Client.action", datatype: "html", data: "idclientencours="+idclientencours, timeout : 8000, error: function() alert( Deconnexion du serveur );, beforesend : function() $("#BoiteFormulaireClient").html( <img src="images/chargementcirculairepetit.gif" align="absmiddle" width="16" height="16" style="padding-right:5px;"/>patientez... );, success: function(html) // mettre le résultat reçu dans la balise de la page courante $("#BoiteFormulaireClient").html(html); // utiliser le formulaire ajaxform $( #Formulaire_Client ).ajaxform( // préciser la cible qui va recevoir la réponse (balise) target: #BoiteReponseAjax, // fonction déclenchée à la suite de l envoi du formulaire success: function() // afficher les réponses (erreurs, erreurs d action, succès) $( #BoiteReponseAjax ).fadein( slow ); // modification réalisée avec succès if($ ( #BoiteReponseAjax ).html().indexof("message_information")!=-1) // récupérer la nouvelle liste de client listerclient(); // rafficher le formulaire de création ajouterclient(); ); ); - 6 -

304 Arborescence du projet exemple25 Gestion des clients en Ajax - 7 -

305 Optimisations L exemple précédent est largement fonctionnel. Nous allons l améliorer dans le projet exemple26, par des services de type Web 2.0 avec le langage DHTML, les feuilles de styles et les plug ins JQuery. Les optimisations utilisées sont les suivantes : Utilisation de boutons dynamiques. Gestion des boîtes (box) dynamiques pour les confirmations et messages. Utilisation du plug in de gestion des composants ou widgets de type googletoolkit. Utilisation d un calendrier dynamique pour la gestion des dates. Utilisation d un service d autocomplétion pour les recherches. Gestion des tris dynamiques. 1. Utilisation de boutons dynamiques Nous allons ajouter un nouveau style pour le bouton de validation avec une icône afin d afficher l image de chargement dans le bouton à la manière d un logiciel. #boutonvalider background-image: url(../images/valider.png); background-repeat:no-repeat; background-position:5% 65%; height:20px; padding-left:20px; width:120px; height:25px; color:#333333; font-family: tahoma, verdana, arial, sans-serif; font-size:11px; font-weight:normal; margin-left:17px; background-color:#ebebeb; border:1px solid; border-top-color:#999999; border-left-color:#999999; border-right-color:#666666; border-bottom-color:#666666; Ensuite, nous ajoutons les lignes suivantes JQuery permettant de changer l image de fond du bouton, de le désactiver et de l afficher à nouveau lors de la réponse. // utiliser le formulaire ajaxform $( #Formulaire_Client ).ajaxform(... // fonction déclenchée avant l envoi du formulaire beforesubmit : function() $("#boutonvalider").css("backgroundimage","url(./images/chargementcirculairepetit.gif)"); $("#boutonvalider").attr("disabled","disabled");, // fonction déclenchée à la suite de l envoi du formulaire success: function() // remettre le bouton $("#boutonvalider").css("backgroundimage","url(./images/valider.png)"); - 1 -

306 $("#boutonvalider").removeattr("disabled");... Utilisation de boutons dynamiques 2. Gestion des boîtes (box) dynamiques pour les confirmations et messages Nous allons ajouter un nouveau plug in JQuery nommé JQueryUI ( Ce plug in permet de gérer des calendriers, barres de chargement, tableaux ou autres. La librairie est copiée dans le répertoire /javascript/jqueryui avec le répertoire /css pour les feuilles de styles et le répertoire /js pour les fichiers JavaScript. Nous allons utiliser la librairie pour créer des boîtes de dialogues dynamiques Web 2.0 avec des boutons, afin de gérer le redimensionnement des boîtes, le déplacement des boîtes et la gestion de la mise au premier plan. Nous passons ensuite à la mise en place des librairies dans la page /jsp/indexclient.jsp : <!-- Utiliser la librairie JavaScript JQueryUI --> <style type="text/css">@import url(javascript/plugin/jqueryui/css/ smoothness/jquery-ui custom.css);</style> <style type="text/css">@import url(javascript/plugin/jqueryui/css/ css.css);</style> <script type="text/javascript" src="javascript/plugin/jqueryui/js/ jquery-ui custom.min.js"></script> <script type="text/javascript" src="javascript/exemplejqueryui.js"></script> La librairie est en place, nous pouvons modifier nos liens pour la suppression des clients afin de déclencher une nouvelle fonction JavaScript de confirmation évoluée. <a href="javascript:confirmersupprimerclient( $idclient )"><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a> La gestion des boîtes dynamiques est presque terminée, il reste à coder la fonction confirmersupprimerclient (idclientencours) dans le fichier JavaScript /javascript/exemplejform.js permettant d afficher la boîte de dialogue modale. Cette boîte est affichée au centre (position) et possède deux boutons (Ok et Annuler). Le bouton de confirmation déclenche la méthode supprimerclient(idclientencours). // fonction qui permet d afficher une fenêtre de confirmation avant de supprimer - 2 -

307 function confirmersupprimerclient(idclientencours) $(function() // Configuration de la boîte $( #BoiteConfirmation ).dialog( autoopen: false, width: 400, modal: true, position: middle, buttons: "Ok": function() $ (this).dialog("close"); supprimerclient(idclientencours);, "Annuler": function() $ (this).dialog("close"); ); ); // Afficher la boîte au clic souris $( #BoiteConfirmation ).dialog( open ); La page principale /jsp/indexclient.jsp est légèrement modifiée pour accueillir la boîte de dialogue cachée au départ de l application. <!-- boîte utilisée pour les confirmations --> <div id="boiteconfirmation" title="gestion des clients" style="display:none"> <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>Voulez-vous supprimer le client?</p> </div> Boîte de dialogue Web 2.0 Pour la gestion des boîtes de messages (erreurs, erreurs d actions et succès), nous allons utiliser un code JavaScript basé sur un Timer (thread) qui va afficher ou cacher les boîtes suivant les besoins. // temps d attente des messages en millisecondes var tempsattente=3000; // fonction qui permet d afficher ou cacher les messages pendant un certain temps - 3 -

308 function messagedynamique(cache,animation) if($("#message_erreur")!=null $ ("#message_information")!=null) if(cache) $("#message_erreur").effect("explode",,500); $("#message_information").effect("explode",,500); else $("#message_erreur").show("slow"); $("#message_information").show("slow"); if(animation) settimeout("messagedynamique("+! cache+",false)",tempsattente); Cette méthode utilise donc un temps d attente de 3 secondes (paramétrable) afin de masquer les messages avec un effet d animation. La fonction messagedynamique(cache,animation) est ensuite appelée dans chaque méthode retournant une valeur.... // fonction déclenchée à la suite de l envoi du formulaire success: function() // afficher les réponses (erreurs, erreurs d action, succès) $( #BoiteReponseAjax ).fadein( slow ); // modification réalisée avec succès if($ ( #BoiteReponseAjax ).html().indexof("message_information")!=-1) // récupérer la nouvelle liste de client listerclient(); // réafficher le formulaire de création ajouterclient(); // gestion dynamique des messages messagedynamique(false,true); Utilisation du plug in Widget Les composants ou widgets sont de plus en plus utilisés dans les applications Internet. Nous retrouvons alors des boîtes qui peuvent être agrandies, déplacées, fermées et qui se rapprochent de l utilisation des logiciels. Le plug in JQuery easywidgets permet de créer ces composants évolués. Ce plug in utilise une feuille de styles spécifique ainsi qu un fichier JavaScript jquery.easywidgets.js et des images pour les boîtes

309 Arborescence JavaScript du plug in JQuery easywidgets La gestion du plug in est ensuite réalisée avec du code JavaScript placé dans notre fichier /javascript/exemple26.js. Pour créer un widget, nous devons utiliser les balises <div/> avec des classes spécifiques et nommer de façon unique avec l attribut id, chaque widget afin de pouvoir y faire référence. La boîte englobante doit être associée à la classe CSS widget-place, la sous boîte englobante est liée à la classe widget et aux différentes propriétés à gérer (movable, editable, removable ). Le bloc du titre est associé au style widget-header, la sous boîte possède le style widgeteditbox et enfin le contenu principal est lié au style widget-content. Pour résumer, la structure d un widget (composant) est la suivante : Structure des balises et styles CSS du plug in easywidget Nous allons modifier notre page principale en insérant le titre dans la balise widget-header, le formulaire d ajout dans la partie widget editbox et le tableau de liste dans la section widget content. Nous allons ajouter également un second widget d aide pour montrer la puissance des déplacements des boîtes. Deux liens sont insérés pour masquer ou afficher toutes les boîtes. Le nouveau code de la page JSP est présenté ci dessous. Code : /jsp/indexclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> <!-- Utiliser la librairie JavaScript JQuery --> <script src="javascript/jquery.min.js" type="text/javascript"></script> <script src="javascript/jquery.form.js" type="text/javascript"></script> <!-- fichier de gestion --> - 5 -

310 <script src="javascript/exemple26.js" type="text/javascript"></script> <!-- Utiliser la librairie JavaScript JQueryUI --> <style url(javascript/plugin/jqueryui/css/ smoothness/jquery-ui custom.css);</style> <style url(javascript/plugin/jqueryui/css/ css.css);</style> <script type="text/javascript" src="javascript/plugin/jqueryui/js/ jquery-ui custom.min.js"></script> <script type="text/javascript" src="javascript/exemplejqueryui.js"></script> <!-- Utiliser la librairie JavaScript easywidgets --> <style url(javascript/plugin/easywidgets/css/jquery.easywidgets.css);</st yle> <style url(javascript/plugin/easywidgets/css/example.css);</style> <script type="text/javascript" src="javascript/plugin/easywidgets/ js/jquery.easywidgets.js"></script> <script type="text/javascript" src="javascript/plugin/easywidgets/ js/jquery-ui-min.js"></script> </head> <body> <!-- balise utilisée pour afficher les réponses Ajax --> <div id="boitereponseajax"></div> <div align="left" style="margin-left:20px"> <ul> <li><a onclick="$.fn.showeasywidgets(); return false" href="#" title="afficher les widgets">afficher les widgets</a></li> <li><a onclick="$.fn.hideeasywidgets(); return false" href="#" title="cacher les widgets">cacher les widgets</a></li> </ul> </div> <!-- Widget 1 --> <div class="widget-place" id="widget-place-1"> <div class="widget movable collapsable removable closeconfirm editable collapse" id="identifierwidget-1"> <div class="widget-header"><strong>gestion des clients</strong></div> <div class="widget-editbox"> <!-- balise utilisée pour ajouter/modifier un client en Ajax --> <div id="boiteformulaireclient"></div> </div> <div class="widget-content"> <!-- balise utilisée pour afficher la liste des clients en Ajax --> <div id="boitelisterclient"></div> </div> </div> </div> <!-- Widget 2 --> <div class="widget-place" id="widget-place-2"> <div class="widget movable collapsable removable closeconfirm collapse" id="identifierwidget-2"> <div class="widget-header"><strong>aide</strong></div> <div class="widget-content"> L application de gestion des clients, de type Web 2.0 est totalement gérée en Ajax. </div> </div> </div> <!-- boite utilisée pour les confirmations --> <div id="boiteconfirmation" title="gestion des clients" - 6 -

311 style="display:none"> <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>Voulez-vous supprimer le client?</p> </div> </body> </html> La gestion des widgets est réalisée à l aide du fichier JavaScript exemple26.js. Nous ajoutons le bloc d internationalisation i18n de gestion des textes et images affichés pour la manipulation des boîtes. Nous utilisons également le service de gestion des cookies permettant de retenir le placement de nos widgets (les placements des boîtes) lors de la prochaine utilisation. // Gestion des widgets $(function() // Gestion de l internationalisation pour les boîtes $.fn.easywidgets( i18n : edittext : <img src="./javascript/plugin/easywidgets/images/edit.png" alt="edit" width="16" height="16" title="editer"/>, closetext : <img src="./javascript/plugin/easywidgets/images/close.png" alt="close" width="16" height="16" title="fermer"/>, collapsetext : <img src="./javascript/plugin/easywidgets/images/collapse.png" alt="close" width="16" height="16" title="fermer cet outil"/>, canceledittext : <img src="./javascript/plugin/easywidgets/ images/edit.png" alt="edit" width="16" height="16" />, extendtext : <img src="./javascript/plugin/easywidgets/images/extend.png" alt="close" width="16" height="16" title="ouvrir cet outil"/>, confirmmsg : Fermer cet outil?, ); // Utilisation du plugin cookie pour mémoriser les placements $.fn.easywidgets( ); ); behaviour : usecookies : true Le service est totalement opérationnel, nous pouvons déplacer les boîtes, les ouvrir, afficher le formulaire d édition ou autre. Nous pouvons également placer les boîtes à notre guise, fermer le navigateur et revenir sur la page pour constater que les fenêtres sont toujours correctement placées

312 Utilisation de widgets pour les formulaires 4. Utilisation d outils dynamiques Pour cette partie du projet Struts 2 et Ajax, nous allons créer un nouveau projet exemple27 à partir du précédent et ajouter deux attributs à la classe JavaBean Client afin de gérer la date de naissance du client et un champ description pour ses informations (cv, compétences ). Ces deux nouvelles propriétés seront par la suite associées à des services dynamiques DHTML afin d afficher un calendrier pour la date de naissance. Le modèle est légèrement modifié pour l initialisation et la modification des objets client. Code : /exemple27/javabeans.client.java package public class Client private int idclient; private String identifiant; private String motdepasse; private String datedenaissance; private String description; public Client() - 8 -

313 public Client(int idclient,string identifiant, String motdepasse, String datedenaissance, String description) this.idclient=idclient; this.identifiant=identifiant; this.motdepasse=motdepasse; this.datedenaissance=datedenaissance; this.description=description; // getter et setter... Code : /exemple27/modele.clientmodele.java package exemple27.modele; import java.util.arraylist; import java.util.list; import exemple27.javabeans.client; public class ClientModele private static List<Client> listeclients; private static int id=1; static listeclients=new ArrayList<Client>(); listeclients.add(new Client(id++, "jlafosse", "jerome", "01/01/1968", "informaticien")); listeclients.add(new Client(id++, "astapane", "amelie", "02/02/2004", "comptable")); listeclients.add(new Client(id++, "amartin", "alain", "16/05/1954", "livreur")); listeclients.add(new Client(id++, "pleroy", "pierre", "04/05/1992", "musicien")); // modifier un client dans la liste public static void modifier(client client) int idclient=client.getidclient(); for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) c.setidentifiant(client.getidentifiant()); c.setmotdepasse(client.getmotdepasse()); c.setdatedenaissance(client.getdatedenaissance());... c.setdescription(client.getdescription()); break; Le code de la vue responsable de l affichage est légèrement modifié afin de gérer la date de naissance et la description du client. Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot - 9 -

314 de passe</b></td><td><b>date de naissance</b></td><td><b>description</b></td><td colspan="2" align="center"><b>gestion</b></td></tr> <s:iterator value="listeclients" status="ligne"> <s:if test="#ligne.odd"><tr class="ligne1"></s:if> <s:if test="#ligne.even"><tr class="ligne2"></s:if> <td><s:property value="idclient"/></td> <td><s:property value="identifiant"/></td> <td><s:property value="motdepasse"/></td> <td><s:property value="datedenaissance"/></td> <td><s:property value="description"/></td> <td align="center"><a href="javascript:modifierclient( $ idclient )"><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="javascript:confirmersupprimerclient( $idclient )"><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> </s:iterator> </table> Nous pouvons désormais passer à la partie dynamique du formulaire client, en modifiant la page JSP /jsp/formulaireclient.jsp afin d ajouter un calendrier dynamique géré par le plug in JQuery UI ( Pour cela nous devons ajouter le code JavaScript permettant d appliquer le calendrier à un objet à partir de son attribut id et nous paramétrons la langue en français à l aide du code JavaScript et de la librairie jquery ui i18n.js. La taille du calendrier est gérée par le style css ui datepicker div. #ui-datepicker-div font-size:8%; Code : /jsp/formulaireclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <!-- formulaire en ajout ou modification --> <s:if test="client.idclient!=0"> <h3>editer un client</h3> <s:set id="action">modifier_client.action</s:set> </s:if> <s:else> <h3>ajouter un client</h3> <s:set id="action">ajouter_client.action</s:set> </s:else> <s:form method="post" action="%action" id="formulaire_client" name="formulaire_client"> <s:hidden key="client.idclient"/> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:textfield name="client.datedenaissance" id="client_datedenaissance" label="date de naissance" labelposition="top" cssclass="input"/> <s:textarea name="client.description" id="client.description" label="description" labelposition="top" cssclass="textarea"/> <s:submit value="valider" id="boutonvalider"/> </s:form> <script type="text/javascript" src="javascript/plugin/jqueryui/js/ i18n/jquery-ui-i18n.js"></script> <script type="text/javascript"> $(function() // afficher les mois et années $("#client_datedenaissance").datepicker( changemonth: true, changeyear: true

315 ); // mettre en langue française $ ("#client_datedenaissance").datepicker($.datepicker.regional[ fr ] ); $("#client_datedenaissance").datepicker( option, $.extend(showmonthafteryear: false, $.datepicker.regional[ fr ])); ); </script> La notation pointée (client.datedenaissance) n est pas autorisée avec le framework JQuery pour accéder à un objet. Nous utilisons dans l exemple le caractère underscore (_). Utilisation d un calendrier dynamique 5. Utilisation d un service d autocomplétion pour les recherches La gestion de l autocomplétion ou proposition lors des recherches est un service courant dans les applications Internet Web 2.0. Pour réaliser ce service, nous allons utiliser à nouveau un plug in JQuery nommé autocomplete avec ses fichiers JavaScript et sa feuille de styles. La mise en place commence par l installation des fichiers dans le répertoire /javascript/plugin de l application exemple27. La vue spécialisée dans l affichage de la liste des clients est modifiée afin de gérer le formulaire de recherche. Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <!-- Formulaire de recherche --> <div id="rechercher" align="left"> <a href="javascript:afficherrecherche();"><img

316 src="images/louperecherche.png" title="cliquer pour afficher le formulaire de recherche" alt="rechercher" align="absmiddle" border="0"/></a> </div> <s:form method="post" id="formulairerecherche" name="formulairerecherche" theme="simple"> <table cellspacing="4" cellpadding="0" id="tableau" width="440px" border="0"> <tr> <td>rechercher : <s:textfield name="recherche" id="recherche" label="recherche" labelposition="left" cssclass="input"/></td> <td> <select name="typerecherche" id="typerecherche" onchange="changertyperecherche();" class="listederoulante"> <option value="client.identifiant">identifiant</option> <option value="client.motdepasse">mot de passe</option> <option value="client.datedenaissance">date de naissance</option> </select> </td> <td><input type="image" src="images/rechercher.gif" align="absmiddle" onclick="javascript:listerclient( recherche )"/></td> </tr> </table> </s:form> <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot de passe</b></td><td><b>date de naissance</b></td><td><b>description</b></td><td colspan="2" align="center"><b>gestion</b></td></tr> <s:iterator value="listeclients" status="ligne"> <s:if test="#ligne.odd"><tr class="ligne1"></s:if> <s:if test="#ligne.even"><tr class="ligne2"></s:if> <td><s:property value="idclient"/></td> <td><s:property value="identifiant"/></td> <td><s:property value="motdepasse"/></td> <td><s:property value="datedenaissance"/></td> <td><s:property value="description"/></td> <td align="center"><a href="javascript:modifierclient( $ idclient )"><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="javascript:confirmersupprimerclient( $idclient )"><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> </s:iterator> </table> <script type="text/javascript"> // Gestion des recherches $(function() if($("#typerecherche")!= null) // Déclenchement de l action pour initialiser l autocomplétion changertyperecherche(); var recherche=$( #recherche ).val(); if(recherche == null recherche == ) $("#formulairerecherche").hide(); else $("#formulairerecherche").show();

317 ); </script> La page responsable de l affichage des termes de l autocomplétion est très simple et permet d afficher chaque réponse à l aide d une collection. Code : /jsp/autocomplete.jsp <%@ page language="java" contenttype="text/html; charset=iso " pageencoding="iso "%> <%@ taglib prefix="s" uri="/struts-tags" %> <s:iterator value="liste" > <s:property/> </s:iterator> Le code JavaScript de gestion du formulaire est modifié afin de gérer l affichage du formulaire de recherche et la validation de l envoi. La fonction listerclient() gère désormais la recherche par autocomplétion. Code : /javascript/exemple27.js... // lancer la recherche par type de champ function changertyperecherche() var attribut=$( #typerecherche ).val(); if(attribut!=null) attribut=jquery.trim(attribut); if(attribut!= ) url="autocomplete.action?attribut="+attribut; $("#recherche").autocomplete(url, delay: 400, width:400, cachelength:1, matchsubset:false, mustmatch : true, minchars:1, autofill: false ); // affichage du formulaire de recherche function afficherrecherche() if($("#formulairerecherche").is(":hidden")) $("#formulairerecherche").slidedown("fast"); else $("#formulairerecherche").slideup("fast"); // retourner la liste des clients en utilisant Ajax function listerclient() //recherche de l utilisateur var recherche=$("#recherche").val(); var attribut=$( #typerecherche ).val(); // pas de recherche if($("#recherche").html()==null $ ("#typerecherche").html()==null) recherche=""; attribut="";

318 //envoyer les donnees en POST $.ajax( type: "POST", url: "Lister_Client.action", datatype: "html", data: "recherche="+recherche+"&attribut="+attribut+"&idclientencours=0", timeout : 8000, error: function() alert( Deconnexion du serveur );, beforesend : function() $("#BoiteListerClient").html( <img src="images/chargementcirculairepetit.gif" align="absmiddle" width="16" height="16" style="padding-right:5px;"/>patientez... );, success: function(html) // mettre le résultat reçu dans la balise de la page courante $("#BoiteListerClient").html(html); if(recherche!="") // gestion dynamique des messages //messagedynamique(false,true); ); // lister tous les clients function listerclientinitialiser() // vider les champs et déclencher la liste $("#recherche").val(""); $( #typerecherche ).val(""); listerclient();... Enfin, le modèle ClientModele est modifié afin de gérer les recherches de l utilisateur dans la liste des clients et retourne uniquement les objets concernés. Code : /exemple27/modele/clientmodele.java package exemple27.modele; import java.util.arraylist; import java.util.list; import exemple27.javabeans.client; public class ClientModele private static List<Client> listeclients; private static int id=1; static listeclients=new ArrayList<Client>(); listeclients.add(new Client(id++, "jlafosse", "jerome", "01/01/1968", "informaticien")); listeclients.add(new Client(id++, "astapane", "amelie", "02/02/2004", "comptable")); listeclients.add(new Client(id++, "amartin", "alain", "16/05/1954", "livreur")); listeclients.add(new Client(id++, "pleroy", "pierre", "04/05/1992", "musicien"));

319 // retourner la liste des clients public static List<Client> getlisteclients(string recherche, String attribut) // liste pour les recherches List<Client> listeclientsrecherche=new ArrayList<Client>(); // recherche utilisateur if(recherche!=null &&!recherche.equalsignorecase("")) // parcourir chaque client for(client c : listeclients) // recherche sur le mot de passe if(attribut.equals("client.motdepasse")) if(c.getmotdepasse().contains(recherche)) listeclientsrecherche.add(c); // recherche sur la date else if(attribut.equals("client.datedenaissance")) if(c.getdatedenaissance().contains(recherche)) listeclientsrecherche.add(c); // recherche sur l identifiant else if(c.getidentifiant().contains(recherche)) listeclientsrecherche.add(c); // retourner la nouvelle liste de clients return listeclientsrecherche; return listeclients; public static void setlisteclients(list<client> listeclients) ClientModele.listeClients = listeclients; // ajouter un client dans la liste public static void ajouter(client client) client.setidclient(id++); listeclients.add(client); // supprimer un client dans la liste public static void supprimer(int idclient) for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) listeclients.remove(c);

320 // modifier un client dans la liste public static void modifier(client client) int idclient=client.getidclient(); for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) c.setidentifiant(client.getidentifiant()); c.setmotdepasse(client.getmotdepasse()); c.setdatedenaissance(client.getdatedenaissance()); c.setdescription(client.getdescription()); break; // trouver un client dans la liste public static Client getclient(int idclient) for(int i=0;i<listeclients.size();i++) Client c=listeclients.get(i); if(c.getidclient()==idclient) return c; return null; // realiser une recherche dans les listes public static ArrayList<String> rechercher(string saisie, String attribut) // liste des recherches ArrayList<String> listerecherches=new ArrayList<String>(); // parcourir chaque client for(client c : listeclients) // recherche sur le mot de passe if(attribut.equals("client.motdepasse")) if(c.getmotdepasse().contains(saisie)) listerecherches.add(c.getmotdepasse().trim()); // recherche sur la date else if(attribut.equals("client.datedenaissance")) if(c.getdatedenaissance().contains(saisie)) listerecherches.add(c.getdatedenaissance());

321 // recherche sur l identifiant else if(c.getidentifiant().contains(saisie)) listerecherches.add(c.getidentifiant().trim()); return listerecherches; L application est opérationnelle et les recherches par autocomplétion permettent de bénéficier d un projet plus ergonomique et fonctionnel. Arborescence du projet exemple

322 Utilisation de l autocomplétion pour les recherches 6. Gestion des tris dynamiques Le service sera finalisé avec l utilisation du plug in JQuery tablesorter ( permettant de trier les colonnes d un tableau. Ce plug in est installé dans le répertoire /javascript/plugin/tablesorter. Afin d activer les tris, les tableaux doivent avoir un identifiant unique (paramètre id) ainsi qu une mise en forme à partir des balises <thead/> et <tbody/>. Notre page d affichage est légèrement modifiée afin de respecter cette structure et de positionner en JavaScript les tris sur le tableau des clients. Code : /jsp/listerclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <!-- Formulaire de recherche --> <div id="rechercher" align="left"> <a href="javascript:afficherrecherche();"><img src="images/louperecherche.png" title="cliquer pour afficher le formulaire de recherche" alt="rechercher" align="absmiddle" border="0"/></a> </div> <s:form method="post" id="formulairerecherche" name="formulairerecherche" theme="simple"> <table cellspacing="4" cellpadding="0" class="tableau" width="440px" border="0"> <tr> <td>rechercher : <s:textfield name="recherche" id="recherche" label="recherche" labelposition="left" cssclass="input"/></td> <td> <select name="typerecherche" id="typerecherche" onchange="changertyperecherche();" class="listederoulante"> <option value="client.identifiant">identifiant</option> <option value="client.motdepasse">mot de passe</option> <option value="client.datedenaissance">date de naissance</option> </select> </td> <td><input type="image" src="images/rechercher.gif" align="absmiddle" onclick="javascript:listerclient( recherche )"/></td> </tr> </table>

323 </s:form> <br/><div align="left"><a href="javascript:listerclientinitialiser();"><img src="images/client.png" align="absmiddle" border="0"/> Lister tous les clients</a></div> <table border="0" id="tableauclient" cellpadding="0" cellspacing="0"> <thead><tr> <th>id</th> <th>identifiant</th> <th>mot de passe</th> <th>date de naissance</th> <th>description</th> <td colspan="2" align="center"><b>gestion</b></td> </tr> </thead> <tbody> <s:iterator value="listeclients" status="ligne"> <s:if test="#ligne.odd"><tr class="ligne1"></s:if> <s:if test="#ligne.even"><tr class="ligne2"></s:if> <td><s:property value="idclient"/></td> <td><s:property value="identifiant"/></td> <td><s:property value="motdepasse"/></td> <td><s:property value="datedenaissance"/></td> <td><s:property value="description"/></td> <td align="center"><a href="javascript:modifierclient( $ idclient )"><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="javascript:confirmersupprimerclient( $idclient )"><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> </s:iterator> </tbody> </table> <script type="text/javascript"> // Gestion des recherches $(function() if($("#typerecherche")!= null) // Déclenchement de l action pour initialiser l autocomplétion changertyperecherche(); var recherche=$( #recherche ).val(); if(recherche == null recherche == ) $("#formulairerecherche").hide(); else $("#formulairerecherche").show(); // Gestion des tris $("#tableauclient").tablesorter(); ); </script>

324 Utilisation du plug in JavaScript de gestion des tris

325 En résumé Ce chapitre a présenté la mise en place de services Web 2.0 à l aide du langage JavaScript et de la technologie Ajax. Les principaux services utilisés dans les applications professionnelles sont présentés. Les formulaires XHTML sans rechargement des pages sont présentés en détail ainsi que les boîtes de dialogues et messages. Le paragraphe suivant propose l utilisation de calendrier dynamique pour la gestion des dates. Enfin, les deux derniers paragraphes introduisent les mécanismes d autocomplétion et de tris dynamiques sans rechargement des pages

326 Velocity Les moteurs de templates permettent d améliorer le code et de séparer la partie traitement des données de la mise en page XHTML. Les moteurs de templates ou de mises en formes apportent des solutions pour la mise en page par l intermédiaire de balises dédiées. Un moteur permet d insérer du contenu dynamique et de gérer le découpage des parties de pages. L objectif de ces moteurs étant de séparer la logique métier de la logique d affichage. Le moteur Velocity ( du consortium Apache est un produit OpenSource fournit en standard avec Struts 2 dans le paquetage struts2 core 2.x.x.jar. Les applications utilisent en standard les pages JSP pour la gestion des vues et la mise en page. Néanmoins, le moteur Velocity (ainsi que FreeMarker) peut être utilisé pour la manipulation des données. L accès aux données et l appréhension de l outil sont assez proches du langage JSP. Velocity permet de placer les vues dans l application elle même et de les empaqueter, au contraire des pages JSP. De même, si nous souhaitons déployer une application en tant que plug in Struts, Velocity permet d inclure les vues dans le même paquetage.jar. Velocity utilise le signe dollar ($) pour l accès aux données. Les résultats de type velocity sont définis en standard avec Struts dans le fichier struts default.xml et peuvent donc être utilisés sans problème

327 Utilisation de Velocity Tout comme JSP, Velocity propose des objets implicites qui peuvent être utilisés sans création préalable. response : l objet HttpServletResponse. res : l alias de l objet response. request : l objet HttpServletRequest. req : l alias de l objet request. session : l objet HttpSession. application : l objet ServletContext. base : le chemin du contexte de l application (path). action : l objet action. stack : la valeur de la pile d exécution. Velocity propose également plusieurs balises ou tags. Ces balises ressemblent aux tags Struts mais la syntaxe est différente et repose sur l utilisation des caractères #nom. Lorsque les pages utilisent uniquement des tags Velocity, la balise d inclusion de la librairie Struts n est plus obligatoire <%@ taglib prefix="s" uri="/struts-tags" %>. L empaquetage de projets avec des pages JSP n est pas possible étant donné que les pages JSP ont besoin du moteur Java pour s exécuter. Voici une liste non exhaustive de tags utilisés avec Velocity : #if, #else, #elseif, #end, #foreach, #include, #stextfield, #sform. Pour utiliser Velocity avec la totalité des fonctionnalités, il est nécessaire de télécharger les librairies suivantes au format.jar et de les installer dans le répertoire /WEB INF/lib de l application. velocity x.jar : la librairie principale du moteur de templates. velocity x dep.jar : la librairie avec les dépendances. velocity tools x.jar : les outils pour le moteur de templates. commons digester x.jar : la librairie permettant de lire des fichiers de configuration au format XML. Pour illustrer l utilisation du moteur de templates Velocity, nous allons créer un nouveau projet exemple28 à partir de l application exemple14. Le fichier de gestion de l application struts.xml est modifié afin de retourner des résultats de type velocity. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple28" namespace="/" extends="strutsdefault"> - 1 -

328 <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple28.clientaction" method="lister"> <result type="velocity">/velocity/listerclient.vm</result> </action> <action name="ajouter_client" class="exemple28.clientaction" method="ajouter"> <result name="input" type="velocity">/velocity/listerclient.vm</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="editer_client" class="exemple28.clientaction" method="editer"> <interceptor-ref name="paramsprepareparamsstack"/> <result name="success" type="velocity">/velocity/editerclient.vm</result> </action> <action name="modifier_client" class="exemple28.clientaction" method="modifier"> <result name="input" type="velocity">/velocity/editerclient.vm</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="supprimer_client" class="exemple28.clientaction" method="supprimer"> <result name="success" type="redirectaction">lister_client</result> </action> </package> </struts> Les pages JSP ne sont plus utilisées, mais des fichiers de templates Velocity au format.vm les remplacent, avec une syntaxe plus simple gérant également en standard l affichage des messages Struts (erreurs de formulaires, erreurs d actions et succès). Code : /velocity/listerclient.vm <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> #sform ("action=ajouter_client") #stextfield ("name=client.identifiant" "id=client.identifiant" "label=identifiant" "labelposition=top" "cssclass=input") #stextfield ("name=client.motdepasse" "id=client.motdepasse" "label=mot de passe" "labelposition=top" "cssclass=input") #ssubmit ("value=ajouter un client") #end <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot de passe</b></td><td colspan="2" - 2 -

329 align="center"><b>gestion</b></td></tr> #foreach( $name in $listeclients ) <tr> <td>$name.idclient</td> <td>$name.identifiant</td> <td>$name.motdepasse</td> <td align="center"><a href="editer_client.action? idclientencours=$name.idclient"/><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="supprimer_client.action? idclientencours=$name.idclient"/><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> #end </table> </div> </body> </html> Code : /velocity/editerclient.vm <html> <head> <title>editer un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>editer un client</h3> #sform ("action=modifier_client") #stextfield ("name=client.identifiant" "id=client.identifiant" "label=identifiant" "labelposition=top" "cssclass=input") #stextfield ("name=client.motdepasse" "id=client.motdepasse" "label=mot de passe" "labelposition=top" "cssclass=input") #ssubmit ("value=modifier un client") #end </div> </body> </html> - 3 -

330 Gestion des clients avec le moteur de templates Velocity - 4 -

331 FreeMarker FreeMarker ( tout comme Velocity, est un moteur de templates qui peut être utilisé avec Struts. D ailleurs, la librairie de balises livrée par Struts est basée sur ce moteur. L utilisation de FreeMarker avec Struts ne requiert aucune librairie supplémentaire car l outil est livré en standard avec le framework et la librairie freemarker x.jar. Tout comme Velocity, FreeMarker permet d empaqueter des applications dans une archive au format.jar. FreeMarker propose des objets implicites qui sont les mêmes que ceux utilisés par Velocity. Les tags FreeMarker peuvent être utilisés en plus des balises Struts et proposent la syntaxe <@s.nom/>. Les fichiers FreeMarker possèdent des extensions au format.ftl et les types de résultats sont nommés freemarker. Nous allons créer un nouveau projet exemple29 à partir du précédent en utilisant FreeMarker comme moteur de templates en lieu et place de JSP ou Velocity. Étant donné que nous développons avec le modèle de conception MVC, seul l affichage et son routage sont changés sans remettre en cause toute l application. Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple29" namespace="/" extends="strutsdefault"> <default-action-ref name="lister_client" /> <action name="lister_client" class="exemple29.clientaction" method="lister"> <result type="freemarker">/freemarker/listerclient.ftl</result> </action> <action name="ajouter_client" class="exemple29.clientaction" method="ajouter"> <result name="input" type="freemarker">/freemarker/listerclient.ftl</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="editer_client" class="exemple29.clientaction" method="editer"> <interceptor-ref name="paramsprepareparamsstack"/> <result name="success" type="freemarker">/freemarker/editerclient.ftl</result> </action> <action name="modifier_client" class="exemple29.clientaction" method="modifier"> <result name="input" type="freemarker">/freemarker/editerclient.ftl</result> <result name="success" type="redirectaction">lister_client</result> </action> <action name="supprimer_client" class="exemple29.clientaction" method="supprimer"> <result name="success" type="redirectaction">lister_client</result> </action> </package> </struts> - 1 -

332 Code : /freemarker/listerclient.ftl <html> <head> <title>liste des clients</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <@s.form method="post" action="ajouter_client"> <@s.hidden key="client.idclient"/> <@s.textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <@s.textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <@s.submit value="ajouter un client"/> </@s.form> <table border="0" id="tableau" cellpadding="0" cellspacing="0"> <tr><td><b>id</b></td><td><b>identifiant</b></td><td><b>mot de passe</b></td><td colspan="2" align="center"><b>gestion</b></td></tr> <@s.iterator value="listeclients" status="ligne"> <@s.if test="#ligne.odd"><tr class="ligne1"></@s.if> <@s.if test="#ligne.even"><tr class="ligne2"></@s.if> <td><@s.property value="idclient"/></td> <td><@s.property value="identifiant"/></td> <td><@s.property value="motdepasse"/></td> <td align="center"><a href="editer_client.action? idclientencours=$idclient"/><img src="images/editerclient.png" alt="editer" title="editer" border="0"/></a></td> <td align="center"><a href="supprimer_client.action? idclientencours=$idclient"/><img src="images/supprimerclient.png" alt="supprimer" title="supprimer" border="0"/></a></td> </tr> </@s.iterator> </table> </div> </body> </html> Code : /freemarker/editerclient.ftl <html> <head> <title>editer un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>editer un client</h3> <@s.form method="post" action="modifier_client"> <@s.hidden key="client.idclient"/> <@s.textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" cssclass="input"/> <@s.textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <@s.submit value="modifier un client"/> </@s.form> </div> - 2 -

333 </body> </html> Arborescence du projet exemple29-3 -

334 En résumé Velocity est un moteur de templates utilisé pour la couche Vue du modèle MVC en lieu et place de JSP dans une application Struts. Cet outil est présenté à partir d un exemple concret. Struts utilise également en standard un autre moteur de templates appelé FreeMarker pour sa bibliothèque de balises. Cette librairie utilisée dans une application web peut être totalement empaquetée et portable au format.jar

335 Présentation XML (extended Markup Language) dérive du langage SGML (Standard Generalized Markup Language) développé dans les années 80. Une version plus simple de ce langage a été proposée pour la présentation de document web, le HTML (HyperText Markup Language). XML utilise la simplicité du HTML avec la souplesse de SGML. Dans un document XML, la mise en forme est complètement séparée des données. Les informations (contenu) sont séparées de l apparence (contenant). Nous pourrons donc fournir plusieurs types de sorties pour un même fichier de données (image, fichier HTML, fichier XML, fichier PDF...). XSL (extensible Stylesheet Language) permet de gérer les styles et la mise en forme d un document XML, c est ce que CSS est pour HTML. Les documents XML sont utilisés pour l échange de données ou l utilisation d un standard lors de la structuration des informations. Les données XML peuvent donc être manipulées par les clients et le serveur. Un document XML peut être transformé en un autre document XML (changement des noms de balises, ordonnancement ) ou en document XHTML, texte, PDF ou autre. Le schéma ci dessous présente la technique permettant d obtenir un document HTML/XHTML à partir d un document XML et d une feuille de style XSL et le moteur XSLT. La partie Données sera alors complètement séparée de la partie Affichage. Technique de transformation XML XSLT vers HTML - 1 -

336 Utilisation Nous allons mettre en application plusieurs exemples afin de réaliser des transformations XML vers XML, XML vers flux RSS et XML vers XHTML. Struts propose pour cela un résultat de type XSLT avec des paramètres supplémentaires utilisés pour la feuille de style XSLT. stylesheetlocation : chemin de localisation de la feuille de style. excudingpattern : liste d éléments à exclure à partir d un modèle. matchingpattern : spécification des modèles. parse : chemin de la feuille parsée ou non avec une expression OGNL. Les feuilles de style XSLT sont par défaut dans le cache avec Struts. Pour changer cette configuration, il est nécessaire de modifier le paramètre struts.xslt.nocache dans le fichier struts.properties ou default.properties. Le projet exemple30 permet de créer un fichier XML à partir des saisies utilisateur. Ce fichier XML est mis en forme à partir des saisies dans un formulaire HTML et d une feuille de style XSL. Chaque accès est réalisé automatiquement à partir du nœud result et des nœuds fils (ex : /result/client/identifiant pour client.getidentifiant()). Code : struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <package name="exemple30" namespace="/" extends="strutsdefault"> <default-action-ref name="ajouter_client" /> </package> </struts> <action name="ajouter_client"> <result>/jsp/ajouterclient.jsp</result> </action> <action name="xmlxsl" class="exemple30.clientaction"> <result name="success" type="xslt"> <param name="stylesheetlocation"> /xsl/client.xsl </param> </result> </action> Code : /jsp/ajouterclient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>ajouter un client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>ajouter un client</h3> <s:form method="post" action="xmlxsl" id="formulaire_client" name="formulaire_client"> <s:textfield name="client.identifiant" id="client.identifiant" label="identifiant" labelposition="top" - 1 -

337 cssclass="input"/> <s:textfield name="client.motdepasse" id="client.motdepasse" label="mot de passe" labelposition="top" cssclass="input"/> <s:textfield name="client.datedenaissance" id="client_datedenaissance" label="date de naissance" labelposition="top" cssclass="input"/> <s:textarea name="client.description" id="client_description" label="description" labelposition="top" cssclass="textarea"/> <s:submit value="valider" id="boutonvalider"/> </s:form> </div> </body> </html> Code : exemple30.clientaction.java package exemple30; import com.opensymphony.xwork2.actionsupport; import public class ClientAction extends ActionSupport private Client client; public Client getclient() return client; public void setclient(client client) this.client = client; // traiter les informations public String execute() return SUCCESS; Code : /xsl/client.xsl <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl=" <xsl:template match="/"> <client> <identifiant> <xsl:value-of select="/result/client/identifiant"/> </identifiant> <motdepasse> <xsl:value-of select="/result/client/motdepasse"/> </motdepasse> <datedenaissance> <xsl:value-of select="/result/client/datedenaissance"/> </datedenaissance> <description> <xsl:value-of select="/result/client/description"/> </description> </client> </xsl:template> </xsl:stylesheet> Code : struts.properties struts.xslt.nocache=true - 2 -

338 Arborescence du projet exemple30 Formulaire d ajout d un compte client - 3 -

339 Affichage des informations client au format XML Le second projet exemple31 permet d afficher le résultat sous la forme d un flux RSS en modifiant uniquement la partie VUE, c est à dire la feuille de style XSL. Code : /xsl/client.xsl <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl=" <xsl:template match="/"> <rss version="2.0"> <channel> <title>flux RSS des clients</title> <link> <description>flux RSS des clients</description> <!-- client --> <item> <title> <xsl:value-of select="/result/client/identifiant"/> </title> <description> <xsl:value-of select="/result/client/description"/> </description> </item> </channel> </rss> </xsl:template> </xsl:stylesheet> - 4 -

340 Affichage des informations client au format RSS Le dernier projet exemple32 permet d afficher au format XHTML, les informations saisies par l utilisateur à l aide d une feuille de style XSL. Nous observons alors tout l intérêt d utiliser une séparation entre les traitements et la mise en page. Code : /xsl/client.xsl <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl=" <xsl:output method="html" indent="yes" encoding="iso "/> <xsl:template match="/"> <html> <head> <title>client </title> <!-- feuilles de style --> <link rel="stylesheet" type="text/css" href="css/styles.css" title="defaut" /> </head> <body> <table border="0" id="tableau" cellpadding="0" cellspacing="4"> <tr><td><b>identifiant</b></td><td><b>mot de passe</b></ td><td><b>date de naissance</b></td><td><b>description</b></td></tr> <tr> <td><xsl:value-of select="/result/client/identifiant"/></td> <td><xsl:value-of select="/result/client/motdepasse"/></td> <td><xsl:value-of select="/result/client/datedenaissance"/></td> <td><xsl:value-of select="/result/client/description"/></td> </tr> </table> </body> </html> </xsl:template> </xsl:stylesheet> - 5 -

341 Affichage du compte client avec une feuille XSLT - 6 -

Avant-propos 1. Avant-propos...3 2. Organisation du guide...3 3. À qui s'adresse ce guide?...4

Avant-propos 1. Avant-propos...3 2. Organisation du guide...3 3. À qui s'adresse ce guide?...4 Les exemples cités tout au long de cet ouvrage sont téléchargeables à l'adresse suivante : http://www.editions-eni.fr. Saisissez la référence ENI de l'ouvrage EP5EJAV dans la zone de recherche et validez.

Plus en détail

Projet Java EE Approfondi

Projet Java EE Approfondi EISTI Projet Java EE Approfondi Manuel d installation du framework Stripes Amaury Languillat, Yann Gonzalez, Arnaud Recher, Vincent Laronde, Anys Mechkar 10 Manuel d installation Téléchargement On part

Plus en détail

Serveur d'application Client HTML/JS. Apache Thrift Bootcamp

Serveur d'application Client HTML/JS. Apache Thrift Bootcamp Serveur d'application Client HTML/JS Apache Thrift Bootcamp Pré-requis La liste ci-dessous de logiciels doit être installée et opérationnelle sur la machine des participants : Compilateur thrift http://thrift.apache.org/

Plus en détail

TP JEE Développement Web en Java. Dans ce TP nous commencerons la programmation JEE par le premier niveau d une application JEE : l application web.

TP JEE Développement Web en Java. Dans ce TP nous commencerons la programmation JEE par le premier niveau d une application JEE : l application web. ASTRIUM - Toulouse JEE Formation 2013 TP JEE Développement Web en Java Dans ce TP nous commencerons la programmation JEE par le premier niveau d une application JEE : l application web. Figure 1 Architecture

Plus en détail

Compte Rendu d intégration d application

Compte Rendu d intégration d application ISMA 3EME ANNEE Compte Rendu d intégration d application Compte Rendu Final Maxime ESCOURBIAC Jean-Christophe SEPTIER 19/12/2011 Table des matières Table des matières... 1 Introduction... 3 1. Le SGBD:...

Plus en détail

les techniques d'extraction, les formulaires et intégration dans un site WEB

les techniques d'extraction, les formulaires et intégration dans un site WEB les techniques d'extraction, les formulaires et intégration dans un site WEB Edyta Bellouni MSHS-T, UMS838 Plan L extraction des données pour un site en ligne Architecture et techniques Les différents

Plus en détail

Formation Webase 5. Formation Webase 5. Ses secrets, de l architecture MVC à l application Web. Adrien Grand <jpountz@via.ecp.fr> Centrale Réseaux

Formation Webase 5. Formation Webase 5. Ses secrets, de l architecture MVC à l application Web. Adrien Grand <jpountz@via.ecp.fr> Centrale Réseaux Formation Webase 5 Ses secrets, de l architecture MVC à l application Web Adrien Grand Centrale Réseaux Sommaire 1 Obtenir des informations sur Webase 5 2 Composants de Webase 5 Un

Plus en détail

Web Tier : déploiement de servlets

Web Tier : déploiement de servlets Web Tier : déploiement de servlets 1 / 35 Plan 1 Introduction 2 Servlet : Principe de fonctionnement 3 Création et développement sur un serveur JEE 4 Quelques méthodes de l API des servlets 5 Utilisation

Plus en détail

Cours en ligne Développement Java pour le web

Cours en ligne Développement Java pour le web Cours en ligne Développement Java pour le web We TrainFrance info@wetrainfrance Programme général du cours Développement Java pour le web Module 1 - Programmation J2ee A) Bases de programmation Java Unité

Plus en détail

Mise en œuvre des serveurs d application

Mise en œuvre des serveurs d application Nancy-Université Mise en œuvre des serveurs d application UE 203d Master 1 IST-IE Printemps 2008 Master 1 IST-IE : Mise en œuvre des serveurs d application 1/54 Ces transparents, ainsi que les énoncés

Plus en détail

Magento. Magento. Réussir son site e-commerce. Réussir son site e-commerce BLANCHARD. Préface de Sébastien L e p e r s

Magento. Magento. Réussir son site e-commerce. Réussir son site e-commerce BLANCHARD. Préface de Sébastien L e p e r s Mickaël Mickaël BLANCHARD BLANCHARD Préface de Sébastien L e p e r s Magento Préface de Sébastien L e p e r s Magento Réussir son site e-commerce Réussir son site e-commerce Groupe Eyrolles, 2010, ISBN

Plus en détail

BIRT (Business Intelligence and Reporting Tools)

BIRT (Business Intelligence and Reporting Tools) BIRT (Business Intelligence and Reporting Tools) Introduction Cette publication a pour objectif de présenter l outil de reporting BIRT, dans le cadre de l unité de valeur «Data Warehouse et Outils Décisionnels»

Plus en détail

Java pour le Web. Cours Java - F. Michel

Java pour le Web. Cours Java - F. Michel Java pour le Web Cours Java - F. Michel Introduction à JEE 6 (ex J2EE) Historique Qu'est-ce que JEE JEE : Java Entreprise Edition (ex J2EE) 1. Une technologie outils liés au langage Java + des spécifications

Plus en détail

Devenez un véritable développeur web en 3 mois!

Devenez un véritable développeur web en 3 mois! Devenez un véritable développeur web en 3 mois! L objectif de la 3W Academy est de former des petits groupes d élèves au développement de sites web dynamiques ainsi qu à la création d applications web

Plus en détail

Institut Supérieur de Gestion. Cours pour 3 ème LFIG. Java Enterprise Edition Introduction Bayoudhi Chaouki

Institut Supérieur de Gestion. Cours pour 3 ème LFIG. Java Enterprise Edition Introduction Bayoudhi Chaouki Institut Supérieur de Gestion Cours pour 3 ème LFIG Java Enterprise Edition Introduction Bayoudhi Chaouki 1 Java EE - Objectifs Faciliter le développement de nouvelles applications à base de composants

Plus en détail

Europa. Développement JEE 5. avec Eclipse. K a r i m D j a a f a r. A v e c l a c o n t r i b u t i o n d e O l i v i e r S a l v a t o r i

Europa. Développement JEE 5. avec Eclipse. K a r i m D j a a f a r. A v e c l a c o n t r i b u t i o n d e O l i v i e r S a l v a t o r i Développement JEE 5 avec Eclipse Europa K a r i m D j a a f a r A v e c l a c o n t r i b u t i o n d e O l i v i e r S a l v a t o r i Groupe Eyrolles, 2008, ISBN : 978-2-212-12061-5 5 Le projet WTP (Web

Plus en détail

10. Base de données et Web. OlivierCuré [ocure@univ-mlv.fr]

10. Base de données et Web. OlivierCuré [ocure@univ-mlv.fr] 10. Base de données et Web 313 Evolution de l'information Ordre de grandeur : 314 1Mo : 1 gros roman 200Mo : ce que mémorise un être humain dans sa vie. 900Mo : information contenue dans le génome d'une

Plus en détail

SIO-65291 Page 1 de 5. Applications Web dynamiques. Prof. : Dzenan Ridjanovic Assistant : Vincent Dussault

SIO-65291 Page 1 de 5. Applications Web dynamiques. Prof. : Dzenan Ridjanovic Assistant : Vincent Dussault SIO-65291 Page 1 de 5 1- Objectifs généraux Applications Web dynamiques Prof. : Dzenan Ridjanovic Assistant : Vincent Dussault acquérir les principes et concepts fondamentaux dans le domaine d'applications

Plus en détail

Programmation Web. Madalina Croitoru IUT Montpellier

Programmation Web. Madalina Croitoru IUT Montpellier Programmation Web Madalina Croitoru IUT Montpellier Organisation du cours 4 semaines 4 ½ h / semaine: 2heures cours 3 ½ heures TP Notation: continue interrogation cours + rendu à la fin de chaque séance

Plus en détail

Utilisation de Jakarta Tomcat

Utilisation de Jakarta Tomcat ISI 1022 : Déploiement d applications Web Jean-Noël Sorenti. Année 2002/2003 Déploiement d application Web Utilisation de Jakarta Tomcat ISI 1022 : 1 ISI 1022 : Déploiement d applications Web Une application

Plus en détail

1. Installation d'un serveur d'application JBoss:

1. Installation d'un serveur d'application JBoss: EPITA Ala Eddine BEN SALEM App-Ing2 J2EE T.P. 4 EJB3, Serveur d'application JBoss 1. Installation d'un serveur d'application JBoss: télécharger l'archive du serveur JBoss à l'adresse: http://sourceforge.net/projects/jboss/files/jboss/jboss-5.0.0.ga/jboss-5.0.0.ga.zip/download

Plus en détail

Description des pratiques à adopter pour la mise à jour du layout en utilisant le gestionnaire de conception de Sharepoint 2013

Description des pratiques à adopter pour la mise à jour du layout en utilisant le gestionnaire de conception de Sharepoint 2013 Exemple d utilisation du gestionnaire de conception Description des pratiques à adopter pour la mise à jour du layout en utilisant le gestionnaire de conception de Sharepoint 2013 6, rue de l Etang, L-5326

Plus en détail

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

INTRODUCTION A JAVA. Fichier en langage machine Exécutable INTRODUCTION A JAVA JAVA est un langage orienté-objet pur. Il ressemble beaucoup à C++ au niveau de la syntaxe. En revanche, ces deux langages sont très différents dans leur structure (organisation du

Plus en détail

LANGAGUE JAVA. Public Développeurs souhaitant étendre leur panel de langages de programmation

LANGAGUE JAVA. Public Développeurs souhaitant étendre leur panel de langages de programmation ING 01 LANGAGUE JAVA Durée : 21 heures 1090 HT / jour Dates : à définir en 2012 Concevoir et développer des programmes en langage Java Comprendre le fonctionnement de la machine virtuelle S approprier

Plus en détail

Conception Pascal Cauquil. Modifications Fabrice Benedet. Diffusion : IS, formateurs et participants à la formation Bases de données scientifiques

Conception Pascal Cauquil. Modifications Fabrice Benedet. Diffusion : IS, formateurs et participants à la formation Bases de données scientifiques Date création : 21/05/08 Référence: - Dernière modif. : 25/06/08 Pages 44 Conception Pascal Cauquil Modifications Fabrice Benedet Support de formation de l'initiation Java web Diffusion : IS, formateurs

Plus en détail

JavaServer Pages (JSP)

JavaServer Pages (JSP) JavaServer Pages (JSP) XVIII-1 Prérequis pour ce cours Ce cours a trait à la programmation Java coté serveur Il faut connaître un minimum de technologie J2EE Il faut connaître les servlets XVIII-2 Motivation

Plus en détail

Les architectures 3-tiers Partie I : les applications WEB

Les architectures 3-tiers Partie I : les applications WEB Les architectures 3-tiers Partie I : les applications WEB 1 Evolutions logicielles Des objets aux composants... Objets JavaBeans, Objets ActiveX, Objets COM,... 1 Evolutions logicielles Des objets aux

Plus en détail

Introduction à Eclipse

Introduction à Eclipse Introduction à Eclipse Eclipse IDE est un environnement de développement intégré libre (le terme Eclipse désigne également le projet correspondant, lancé par IBM) extensible, universel et polyvalent, permettant

Plus en détail

Refonte front-office / back-office - Architecture & Conception -

Refonte front-office / back-office - Architecture & Conception - Refonte front-office / back-office - Architecture & Conception - GLG204 - Architectures Logicielles Java 2008/2009 Nom : Cédric Poisson Matricule : 06-49012 Version : 1.0 Jeudi 28 mai 2009 1 / 23 Table

Plus en détail

Un serveur d'archivage

Un serveur d'archivage Un serveur d'archivage destiné au Service Commun de Documentation de l'université de la Méditerranée Encadrement : Noël Novelli Représentants client (S.C.D.) : Axelle Clarisse Ronan Lagadic Equipe Projet

Plus en détail

Présentation du Framework BootstrapTwitter

Présentation du Framework BootstrapTwitter COUARD Kévin HELVIG-LARBRET Blandine Présentation du Framework BootstrapTwitter IUT Nice-Sophia LP-SIL IDSE Octobre 2012 Sommaire I. INTRODUCTION... 3 Définition d'un framework... 3 A propos de BootstrapTwitter...

Plus en détail

Cours Plugin Eclipse. Université Paris VI / Parcours STL / Master I Pierre-Arnaud Marcelot - Iktek - pamarcelot@iktek.com

Cours Plugin Eclipse. Université Paris VI / Parcours STL / Master I Pierre-Arnaud Marcelot - Iktek - pamarcelot@iktek.com Cours Plugin Eclipse Université Paris VI / Parcours STL / Master I Pierre-Arnaud Marcelot - Iktek - pamarcelot@iktek.com 1 Qui suis-je? Ancien étudiant de Jussieu - Paris VI Diplomé du Master Technologies

Plus en détail

CRÉER, ROUTER ET GÉRER UNE NEWSLETTER, UN E-MAILING

CRÉER, ROUTER ET GÉRER UNE NEWSLETTER, UN E-MAILING CRÉER, ROUTER ET GÉRER UNE NEWSLETTER, UN E-MAILING Durée : 3J / 21H Formateur : Consultant expert en PAO et Web-marketing. Groupe de : 4 max Formation au web marketing Objectifs : Mettre en oeuvre des

Plus en détail

CQP Développeur Nouvelles Technologies (DNT)

CQP Développeur Nouvelles Technologies (DNT) ORGANISME REFERENCE STAGE : 26572 20 rue de l Arcade 75 008 PARIS CONTACT Couverture géographique : M. Frédéric DIOLEZ Bordeaux, Rouen, Lyon, Toulouse, Marseille Tél. : 09 88 66 17 40 Nantes, Lille, Strasbourg,

Plus en détail

Présentation de SOFI 2.0

Présentation de SOFI 2.0 Présentation de SOFI 2.0 Version 2.0.3 Présentation SOFI 2.0 1 Agenda Historique Utilisation de SOFI Pourquoi SOFI? 3 Grands Axes Développement Accompagnement et formation Communauté Architecture Les nouveautés

Plus en détail

WEB & DÉVELOPPEMENT LES BASES DU WEB LE LANGAGE HTML FEUILLES DE STYLES CSS HISTORIQUE D INTERNET ET DU WEB LES DIFFÉRENTS LANGAGES

WEB & DÉVELOPPEMENT LES BASES DU WEB LE LANGAGE HTML FEUILLES DE STYLES CSS HISTORIQUE D INTERNET ET DU WEB LES DIFFÉRENTS LANGAGES WEB & DÉVELOPPEMENT LES BASES DU WEB HISTORIQUE D INTERNET ET DU WEB LES DIFFÉRENTS LANGAGES LE LANGAGE HTML STRUCTURE D UNE PAGE En-tête et corps Syntaxe INSÉRER DES CONTENUS Texte : formatage (titre,

Plus en détail

Module BD et sites WEB

Module BD et sites WEB Module BD et sites WEB Cours 8 Bases de données et Web Anne Doucet Anne.Doucet@lip6.fr 1 Le Web Architecture Architectures Web Client/serveur 3-tiers Serveurs d applications Web et BD Couplage HTML-BD

Plus en détail

< Atelier 1 /> Démarrer une application web

< Atelier 1 /> Démarrer une application web MES ANNOTATIONS SONT EN ROUGE : Axel < Atelier 1 /> Démarrer une application web Microsoft France Tutorial Découverte de ASP.NET 2.0 Sommaire 1 INTRODUCTION... 3 1.1 CONTEXTE FONCTIONNEL... 3 1.2 CONTEXTE

Plus en détail

Introduction à la plateforme J2EE

Introduction à la plateforme J2EE Introduction à la plateforme J2EE Auteur : Oussama Essefi Directeur technique Expert Consulting Oussama.essefi@expert-consulting.biz Copyright 2010 Expert Consulting Page 1 1. Introduction 1.1. Pourquoi

Plus en détail

Construction d un Site Internet Dynamique avec Joomla René-Yves Hervé, Ph.D.

Construction d un Site Internet Dynamique avec Joomla René-Yves Hervé, Ph.D. Construction d un Site Internet Dynamique avec Joomla René-Yves Hervé, Ph.D. TABLE DES MATIÈRES I. Présentation de Joomla II. III. IV. Documents disponibles Installation de Joomla 3.1) Installation sur

Plus en détail

TP1. Outils Java Eléments de correction

TP1. Outils Java Eléments de correction c sep. 2008, v2.1 Java TP1. Outils Java Eléments de correction Sébastien Jean Le but de ce TP, sur une séance, est de se familiariser avec les outils de développement et de documentation Java fournis par

Plus en détail

TP JAVASCRIPT OMI4 TP5 SRC1 2011-2012

TP JAVASCRIPT OMI4 TP5 SRC1 2011-2012 TP JAVASCRIPT OMI4 TP5 SRC1 2011-2012 FORMULAIRE DE CONTACT POUR PORTFOLIO PRINCIPE GENERAL Nous souhaitons réaliser un formulaire de contact comprenant les champs suivants : NOM PRENOM ADRESSE MAIL MESSAGE

Plus en détail

Programme «Analyste Programmeur» Diplôme d état : «Développeur Informatique» Homologué au niveau III (Bac+2) (JO N 176 du 1 août 2003) (34 semaines)

Programme «Analyste Programmeur» Diplôme d état : «Développeur Informatique» Homologué au niveau III (Bac+2) (JO N 176 du 1 août 2003) (34 semaines) Programme «Analyste Programmeur» Diplôme d état : «Développeur Informatique» Homologué au niveau III (Bac+2) (JO N 176 du 1 août 2003) (34 semaines) Module 1 : Programmer une application informatique Durée

Plus en détail

Catalogue des Formations Techniques

Catalogue des Formations Techniques Catalogue des Formations Techniques Items Média Concept 4, allées Pierre-Gilles de Gennes - 33700 Mérignac Téléphone : 05.57.35.73.73 Télécopie : 05.57.35.73.70 Courriel : contact@imc-fr.com 2 Préambule

Plus en détail

Catalogue Formations Jalios

Catalogue Formations Jalios Catalogue Formations Jalios Offre de services Jalios 23/04/2015-6.0 1 / 19 Sommaire Sommaire... 2 1. Introduction... 3 2. Jalios, organisme de formation... 4 3. Formations fonctionnelles... 5 3.1. Formation

Plus en détail

Soon_AdvancedCache. Module Magento SOON. Rédacteur. Relecture & validation technique. Historique des révisions

Soon_AdvancedCache. Module Magento SOON. Rédacteur. Relecture & validation technique. Historique des révisions Module Magento SOON Soon_AdvancedCache Rédacteur Hervé G. Lead développeur Magento herve@agence-soon.fr AGENCE SOON 81 avenue du Bac 94210 LA VARENNE ST HILAIRE Tel : +33 (0)1 48 83 95 96 Fax : +33 (0)1

Plus en détail

Joomla! Création et administration d'un site web - Version numérique

Joomla! Création et administration d'un site web - Version numérique Avant-propos 1. Objectifs du livre 15 1.1 Orientation 15 1.2 À qui s adresse ce livre? 16 2. Contenu de l ouvrage 17 3. Conclusion 18 Introduction 1. Un peu d histoire pour commencer... 19 1.1 Du web statique

Plus en détail

Méthode de Test. Pour WIKIROUTE. Rapport concernant les méthodes de tests à mettre en place pour assurer la fiabilité de notre projet annuel.

Méthode de Test. Pour WIKIROUTE. Rapport concernant les méthodes de tests à mettre en place pour assurer la fiabilité de notre projet annuel. Méthode de Test Pour WIKIROUTE Rapport concernant les méthodes de tests à mettre en place pour assurer la fiabilité de notre projet annuel. [Tapez le nom de l'auteur] 10/06/2009 Sommaire I. Introduction...

Plus en détail

7 villa de la citadelle Né le 13 mai 1983 94110 Arcueil Nationalité : Française. Développeur Web JEE COMPÉTENCES

7 villa de la citadelle Né le 13 mai 1983 94110 Arcueil Nationalité : Française. Développeur Web JEE COMPÉTENCES Philippe Crépin 7 villa de la citadelle Né le 13 mai 1983 94110 Arcueil Nationalité : Française : 06.17.46.12.09 : phi.crepin@gmail.com Disponibilité : En poste chez Soft Computing Développeur Web JEE

Plus en détail

Architecture JEE. Objectifs attendus. Serveurs d applications JEE. Architectures JEE Normes JEE. Systèmes distribués

Architecture JEE. Objectifs attendus. Serveurs d applications JEE. Architectures JEE Normes JEE. Systèmes distribués Architecture JEE. Objectifs attendus Serveurs d applications JEE Systèmes distribués Architectures JEE Normes JEE couches logicielles, n-tiers framework JEE et design patterns 2007/02/28 Eric Hébert.eheb@yahoo.fr

Plus en détail

Extension SSO Java. Cette note technique décrit la configuration et la mise en œuvre du filtre de custom SSO Java.

Extension SSO Java. Cette note technique décrit la configuration et la mise en œuvre du filtre de custom SSO Java. Note technique W4 Engine Extension SSO Java Cette note technique décrit la configuration et la mise en œuvre du filtre de custom SSO Java. 1 Présentation 3 2 Custom SSO Java 4 3 Bilan 10 Sommaire Référence

Plus en détail

Application Web et J2EE

Application Web et J2EE Application Web et J2EE Servlet, JSP, Persistence, Méthodologie Pierre Gambarotto Département Informatique et Math appli ENSEEIHT Plan Introduction 1 Introduction Objectfis

Plus en détail

Construire des plug-ins pour SAS Management Console SAS 9.1

Construire des plug-ins pour SAS Management Console SAS 9.1 Construire des plug-ins pour SAS Management Console SAS 9.1 Janvier 2005 Sommaire 1 INTRODUCTION... 3 1.1 OBJECTIFS... 3 1.2 PERIMETRE... 3 2 LES COMPOSANTS DE SAS MANAGEMENT CONSOLE... 4 3 LA CONSTRUCTION

Plus en détail

INTERNET est un RESEAU D ORDINATEURS RELIES ENTRE EUX A L ECHELLE PLANETAIRE. Internet : interconnexion de réseaux (anglais : net = réseau)

INTERNET est un RESEAU D ORDINATEURS RELIES ENTRE EUX A L ECHELLE PLANETAIRE. Internet : interconnexion de réseaux (anglais : net = réseau) CS WEB Ch 1 Introduction I. INTRODUCTION... 1 A. INTERNET INTERCONNEXION DE RESEAUX... 1 B. LE «WEB» LA TOILE, INTERCONNEXION DE SITES WEB... 2 C. L URL : LOCALISER DES RESSOURCES SUR L INTERNET... 2 D.

Plus en détail

S7 Le top 10 des raisons d utiliser PHP pour moderniser votre existant IBM i

S7 Le top 10 des raisons d utiliser PHP pour moderniser votre existant IBM i Modernisation IBM i Nouveautés 2014-2015 IBM Power Systems - IBM i 19 et 20 mai 2015 IBM Client Center, Bois-Colombes S7 Le top 10 des raisons d utiliser PHP pour moderniser votre existant IBM i Mardi

Plus en détail

Bases Java - Eclipse / Netbeans

Bases Java - Eclipse / Netbeans Institut Galilée PDJ Année 2014-2015 Master 1 Environnements Java T.P. 1 Bases Java - Eclipse / Netbeans Il existe plusieurs environnements Java. Il est ESSENTIEL d utiliser la bonne version, et un environnement

Plus en détail

Groupe Eyrolles, 2005, ISBN : 2-212-11406-0

Groupe Eyrolles, 2005, ISBN : 2-212-11406-0 Groupe Eyrolles, 2005, ISBN : 2-212-11406-0 10 L atelier MyEclipse Eclipse facilite considérablement le développement Java en équipe. Son puissant éditeur, associé à des fonctionnalités de complétion de

Plus en détail

Les grandes facettes du développement Web Nicolas Thouvenin - Stéphane Gully

Les grandes facettes du développement Web Nicolas Thouvenin - Stéphane Gully Les grandes facettes du développement Web Qui sommes nous? Nicolas Thouvenin Stéphane Gully Projets Web depuis 2000 LAMP, NodeJS HTML, CSS, jquery

Plus en détail

Comparatif CMS. Laurent BAUREN S Bérenger VIDAL Julie NOVI Tautu IENFA

Comparatif CMS. Laurent BAUREN S Bérenger VIDAL Julie NOVI Tautu IENFA Comparatif CMS Laurent BAUREN S Bérenger VIDAL Julie NOVI Tautu IENFA Sommaire Introduction : Dans le cadre de notre projet de master première année, il nous a été demandé de développer un moteur de recherche

Plus en détail

NFA016 : Introduction. Pour naviguer sur le Web, il faut : Naviguer: dialoguer avec un serveur web

NFA016 : Introduction. Pour naviguer sur le Web, il faut : Naviguer: dialoguer avec un serveur web NFA016 : Introduction O. Pons, S. Rosmorduc Conservatoire National des Arts & Métiers Pour naviguer sur le Web, il faut : 1. Une connexion au réseau Réseau Connexion physique (câbles,sans fils, ) à des

Plus en détail

Serveur d Applications Web : WebObjects

Serveur d Applications Web : WebObjects Serveur d Applications Web : WebObjects Nicolas Roard 29 avril 2004 Table des matières 1 Introduction 1 2 Historique 1 2.1 Implémentation WebObjects....... 2 2.2 Différences et manques?......... 3 3 Principes

Plus en détail

INGÉNIEUR - DÉVELOPPEUR EXPÉRIMENT É JAVA - J2EE. 27 ans - 5 ans d'expérience

INGÉNIEUR - DÉVELOPPEUR EXPÉRIMENT É JAVA - J2EE. 27 ans - 5 ans d'expérience I0049 INGÉNIEUR - DÉVELOPPEUR EXPÉRIMENT É JAVA - J2EE 27 ans - 5 ans d'expérience Expert ises mét iers : Langues : Editeur de logiciels Roumain (Langue maternelle), Russe (Avancé), Anglais (Intermédiaire),

Plus en détail

TP1 : Initiation à Java et Eclipse

TP1 : Initiation à Java et Eclipse TP1 : Initiation à Java et Eclipse 1 TP1 : Initiation à Java et Eclipse Systèmes d Exploitation Avancés I. Objectifs du TP Ce TP est une introduction au langage Java. Il vous permettra de comprendre les

Plus en détail

WysiUpStudio. CMS professionnel. pour la création et la maintenance évolutive de sites et applications Internet V. 6.x

WysiUpStudio. CMS professionnel. pour la création et la maintenance évolutive de sites et applications Internet V. 6.x WysiUpStudio CMS professionnel pour la création et la maintenance évolutive de sites et applications Internet V. 6.x UNE SOLUTION DE GESTION DE CONTENUS D UNE SOUPLESSE INÉGALÉE POUR CRÉER, MAINTENIR ET

Plus en détail

24/11/2011. Cours EJB/J2EE Copyright Michel Buffa. Plan du cours. EJB : les fondamentaux. Enterprise Java Bean. Enterprise Java Bean.

24/11/2011. Cours EJB/J2EE Copyright Michel Buffa. Plan du cours. EJB : les fondamentaux. Enterprise Java Bean. Enterprise Java Bean. Plan du cours 2 Introduction générale : fondamentaux : les fondamentaux Michel Buffa (buffa@unice.fr), UNSA 2002, modifié par Richard Grin (version 1.1, 21/11/11), avec emprunts aux supports de Maxime

Plus en détail

Intégration de l interface graphique de Ptidej dans Eclipse

Intégration de l interface graphique de Ptidej dans Eclipse Intégration de l interface graphique de Ptidej dans Eclipse Driton Salihu (salihudr@iro.umontreal.ca) Lulzim Laloshi (laloshil@iro.umontreal.ca) Département d informatique et de recherche opérationnelle

Plus en détail

Introduction aux «Services Web»

Introduction aux «Services Web» Introduction aux «Services Web» Sana Sellami sana.sellami@univ-amu.fr 2014-2015 Modalité de contrôle de connaissances Note de contrôle de continu Note projet Evaluation du projet la semaine du 17 novembre

Plus en détail

RAPPORT AUDIT SEO. Élaboré à l'attention de : Monsieur Greber Élaboré par : Cédric Peinado

RAPPORT AUDIT SEO. Élaboré à l'attention de : Monsieur Greber Élaboré par : Cédric Peinado - RAPPORT AUDIT SEO Élaboré à l'attention de : Monsieur Greber Élaboré par : Cédric Peinado 17 septembre 2013 Table des matières Optimisation structurelle 2 Optimisation des standards, performances et

Plus en détail

Dans nos locaux au 98 Route de Sauve 30900 NÎMES. Un ordinateur PC par stagiaire, scanner, imprimante/copieur laser couleur

Dans nos locaux au 98 Route de Sauve 30900 NÎMES. Un ordinateur PC par stagiaire, scanner, imprimante/copieur laser couleur FORMATION FORFAIT WEB DEVELOPPEUR Qualification ISQ OPQF Formacode 46 125 Certification de titre professionnel Web Designer + modules optionnels : Développement PHP/MySQL avancé, Web App, CMS e-boutique

Plus en détail

Architecture Orientée Service, JSON et API REST

Architecture Orientée Service, JSON et API REST UPMC 3 février 2015 Précedemment, en LI328 Architecture générale du projet Programmation serveur Servlet/TOMCAT Aujourd hui Quelques mots sur les SOA API - REST Le format JSON API - REST et Servlet API

Plus en détail

PG208, Projet n 3 : Serveur HTTP évolué

PG208, Projet n 3 : Serveur HTTP évolué PG208, Projet n 3 : Serveur HTTP évolué Bertrand LE GAL, Serge BOUTER et Clément VUCHENER Filière électronique 2 eme année - Année universitaire 2011-2012 1 Introduction 1.1 Objectif du projet L objectif

Plus en détail

THÉMATIQUES. Comprendre les frameworks productifs. Découvrir leurs usages. Synthèse

THÉMATIQUES. Comprendre les frameworks productifs. Découvrir leurs usages. Synthèse THÉMATIQUES Comprendre les frameworks productifs Découvrir leurs usages Synthèse 2 LES ENJEUX DES FRAMEWORKS D ENTREPRISE EN 2012 LE CONSTAT Ressources Recrutement Flexibilité Intérêt Montée en compétence

Plus en détail

Environnements de Développement

Environnements de Développement Institut Supérieur des Etudes Technologiques de Mahdia Unité d Enseignement: Environnements de Développement BEN ABDELJELIL HASSINE Mouna m.bnaj@yahoo.fr Développement des systèmes d Information Syllabus

Plus en détail

Technologies Web. Ludovic Denoyer Sylvain Lamprier Mohamed Amine Baazizi Gabriella Contardo Narcisse Nya. Université Pierre et Marie Curie

Technologies Web. Ludovic Denoyer Sylvain Lamprier Mohamed Amine Baazizi Gabriella Contardo Narcisse Nya. Université Pierre et Marie Curie 1 / 22 Technologies Web Ludovic Denoyer Sylvain Lamprier Mohamed Amine Baazizi Gabriella Contardo Narcisse Nya Université Pierre et Marie Curie Rappel 2 / 22 Problématique Quelles technologies utiliser

Plus en détail

Messagerie asynchrone et Services Web

Messagerie asynchrone et Services Web Article Messagerie asynchrone et Services Web 1 / 10 Messagerie asynchrone et Services Web SOAP, WSDL SONT DES STANDARDS EMERGEANT DES SERVICES WEB, LES IMPLEMENTATIONS DE CEUX-CI SONT ENCORE EN COURS

Plus en détail

Formation : Langues : Types d Intervention et Secteurs d Activité :

Formation : Langues : Types d Intervention et Secteurs d Activité : Ismail HACHOUM 142, Rue Georges Pompidou, 59110 La Madeleine - FRANCE Email : ismail.hachoum@gmail.com Tél: +33(0) 650 198 937 27 ans - Marié Permis B Ingénieur Etudes et Développement Java/JEE Formation

Plus en détail

Création d une application JEE

Création d une application JEE Création d une application JEE Rédacteurs : Alexandre Baillif, Philippe Lacomme, Raksmey Phan et Michaël PLAN Date : juillet 2010 Mise à jour : Michaël PLAN Date : octobre 2014 Avertissement : - ce document

Plus en détail

Point sur les solutions de développement d apps pour les périphériques mobiles

Point sur les solutions de développement d apps pour les périphériques mobiles Point sur les solutions de développement d apps pour les périphériques mobiles Par Hugues MEUNIER 1. INTRODUCTION a. Une notion importante : le responsive web design Nous sommes en train de vivre une nouvelle

Plus en détail

Technologies du Web. Créer et héberger un site Web. Pierre Senellart. Page 1 / 26 Licence de droits d usage

Technologies du Web. Créer et héberger un site Web. Pierre Senellart. Page 1 / 26 Licence de droits d usage Technologies du Web Créer et héberger un site Web Page 1 / 26 Plan Planification Choisir une solution d hébergement Administration Développement du site Page 2 / 26 Cahier des charges Objectifs du site

Plus en détail

Automatisation de l administration système

Automatisation de l administration système Automatisation de l administration système Plan Problèmatique : trop de systèmes, trop de solutions Typage des solutions Puppet : gestion de configuration de systèmes Capistrano : déploiement d applications

Plus en détail

Créer un site Web : mode d emploi Sous SPIP, avec le squelette «établissement» de l académie de Versailles

Créer un site Web : mode d emploi Sous SPIP, avec le squelette «établissement» de l académie de Versailles Mission TICE - académie de Versailles 7 nov. 2008 Créer un site Web : mode d emploi Sous SPIP, avec le squelette «établissement» de l académie de Versailles Anne-Cécile Franc Mission TICE académie de Versailles

Plus en détail

3W Academy Programme de Formation Développeur Intégrateur web Total : 400 heures

3W Academy Programme de Formation Développeur Intégrateur web Total : 400 heures 3W Academy Programme de Formation Développeur Intégrateur web Total : 400 heures Objectif global : A l issue de la formation, les stagiaires doivent être opérationnels dans la création d un site internet

Plus en détail

Architecture N-Tier. Ces données peuvent être saisies interactivement via l interface ou lues depuis un disque. Application

Architecture N-Tier. Ces données peuvent être saisies interactivement via l interface ou lues depuis un disque. Application Architecture Multi-Tier Traditionnellement une application informatique est un programme exécutable sur une machine qui représente la logique de traitement des données manipulées par l application. Ces

Plus en détail

Programmation Web. Introduction

Programmation Web. Introduction Programmation Web Introduction 1 Introduction 10 séances 1 h cours + 1h TD Notes : contrôle continu DS 1 TP : note de groupe : rapport + code source + démo TD : note personnelle (=0 si 2 absences non justifiées)

Plus en détail

Alfstore workflow framework Spécification technique

Alfstore workflow framework Spécification technique Alfstore workflow framework Spécification technique Version 0.91 (2012-08-03) www.alfstore.com Email: info@alfstore.com Alfstore workflow framework 2012-10-28 1/28 Historique des versions Version Date

Plus en détail

Flex. Lire les données de manière contrôlée. Programmation Flex 4 Aurélien VANNIEUWENHUYZE

Flex. Lire les données de manière contrôlée. Programmation Flex 4 Aurélien VANNIEUWENHUYZE Flex Lire les données de manière contrôlée 1 Plan Lier les données Stocker les données Valider les données 2 Gérer des données Lier des données La notion de DataBinding est l une des plus importantes du

Plus en détail

A. Architecture du serveur Tomcat 6

A. Architecture du serveur Tomcat 6 Administration du serveur A. Architecture du serveur Tomcat 6 La compréhension de l architecture interne du serveur Tomcat 6 est un pré-requis indispensable pour bien en maîtriser l administration et la

Plus en détail

ECLIPSE ET PDT (Php development tools)

ECLIPSE ET PDT (Php development tools) ECLIPSE ET PDT (Php development tools) Eclipse Eclipse est un IDE (Integrated Development Environment)).C estun projet de la Fondation Eclipse visant à développer tout un environnement de développement

Plus en détail

arcopole Studio Version 3.3

arcopole Studio Version 3.3 arcopole Studio Version 3.3 Documentation d installation Mise à jour depuis une version antérieure Site du programme arcopole : www.arcopole.fr Auteur du document : Esri France Version de la documentation

Plus en détail

SYSTÈMES D INFORMATIONS

SYSTÈMES D INFORMATIONS SYSTÈMES D INFORMATIONS Développement Modx Les systèmes de gestion de contenu Les Content Management Système (CMS) servent à simplifier le développement de sites web ainsi que la mise à jour des contenus.

Plus en détail

REQUEA. v 1.0.0 PD 20 mars 2008. Mouvements d arrivée / départ de personnels Description produit

REQUEA. v 1.0.0 PD 20 mars 2008. Mouvements d arrivée / départ de personnels Description produit v 1.0.0 PD 20 mars 2008 Mouvements d arrivée / départ de personnels Description produit Fonctionnalités L application Gestion des mouvements d arrivée / départ de Requea permet la gestion collaborative

Plus en détail

WordPress Référencement naturel (SEO) Optimiser. son référencement. Daniel Roch. Préface d Olivier Andrieu

WordPress Référencement naturel (SEO) Optimiser. son référencement. Daniel Roch. Préface d Olivier Andrieu Daniel Roch Optimiser son référencement WordPress Référencement naturel (SEO) Préface d Olivier Andrieu Groupe Eyrolles, 2013, ISBN : 978-2-212-13714-9 Table des matières AVANT-PROPOS... 1 Pourquoi ce

Plus en détail

JOnAS Day 5.1. Outils de développements

JOnAS Day 5.1. Outils de développements JOnAS Day 5.1 Outils de développements Agenda Introduction Plugin Eclipse (JOPE) Plugin NetBeans (JOnbAS) Cargo 2 Bull, 2009 JOnAS Day 5.1 Objectifs - Réduire les temps de développement - Construction

Plus en détail

Formation en Logiciels Libres. Fiche d inscription

Formation en Logiciels Libres. Fiche d inscription République Tunisienne Ministère de l'industrie et la Technologie - Secrétariat d'état de la Technologie Unité des Logiciels Libres Formation en Logiciels Libres Fiche d inscription (Une fiche par candidat)

Plus en détail

Optimiser pour les appareils mobiles

Optimiser pour les appareils mobiles chapitre 6 Optimiser pour les appareils mobiles 6.1 Créer un site adapté aux terminaux mobiles avec jquery Mobile... 217 6.2 Transformer son site mobile en application native grâce à PhoneGap:Build...

Plus en détail

SITE WEB E-COMMERCE ET VENTE A DISTANCE

SITE WEB E-COMMERCE ET VENTE A DISTANCE Développement d une application JAVA EE SITE WEB E-COMMERCE ET VENTE A DISTANCE PLAN PROJET Binôme ou monôme (B/M): M Nom & Prénom : AIT NASSER Btissam Email : aitnasser.btissam123@gmail.com GSM : Organisme

Plus en détail

Déployer les Fonts, Icones, et Images avec Forms Services 11G

Déployer les Fonts, Icones, et Images avec Forms Services 11G Déployer les Fonts, Icones, et Images avec Forms Services 11G 1. Le fichier Registry.dat Le fichier Registry.dat permet de gérer les correspondances de font entre celles utilisées pour le développement

Plus en détail

Groupe Eyrolles, 2003, ISBN : 2-212-11317-X

Groupe Eyrolles, 2003, ISBN : 2-212-11317-X Groupe Eyrolles, 2003, ISBN : 2-212-11317-X 3 Création de pages dynamiques courantes Dans le chapitre précédent, nous avons installé et configuré tous les éléments indispensables à la mise en œuvre d une

Plus en détail