Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les...

342

Transcript of Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les...

Page 1: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction
Page 2: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 www.gdawj.com.

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

Struts 2 Le framework de développement d'applications Java EE

Jérôme LAFOSSE

Résumé

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.

- 1 -© ENI Editions - All rigths reserved

Page 3: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 4: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 5: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 6: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 7: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 8: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 9: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 2001. 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.

- 1 -© ENI Editions - All rigths reserved

Page 10: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 2000. 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.

- 2 - © ENI Editions - All rigths reserved

Page 11: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 2.1.6 et Eclipse/Lomboz 3.4. Le site officiel de Struts 2 est accessible à cette adresse : http://struts.apache.org/2.x/

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 (http://apache.cict.fr/struts/examples/struts­2.1.6­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­2.1.6­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.

- 1 -© ENI Editions - All rigths reserved

Page 12: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 : http://localhost:8080/installationstruts2/

Nous pouvons également tester l’application d’authentification fournie en standard : http://localhost:8080/installationstruts2/example/Login.action

- 2 - © ENI Editions - All rigths reserved

Page 13: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire installationstruts2

- 3 -© ENI Editions - All rigths reserved

Page 14: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 struts­plugin.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.

- 1 -© ENI Editions - All rigths reserved

Page 15: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Le plug­in JFreeChart

JFreeChart (http://www.jfree.org/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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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" /> <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> </package> </struts>

Code : exemple33.ClientAction.java

- 1 -© ENI Editions - All rigths reserved

Page 16: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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()); // créer le graphique chart = ChartFactory.createPieChart3D( "Répartition du temps de travail du client", // titre donnees, // données à afficher true, // afficher la légende true, true ); // 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 - © ENI Editions - All rigths reserved

Page 17: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Code : /jsp/TempsClient.jsp <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>R&eacute;partition du temps client</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>R&eacute;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 -© ENI Editions - All rigths reserved

Page 18: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Arborescence du projet exemple33

- 4 - © ENI Editions - All rigths reserved

Page 19: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire de saisie des valeurs client

- 5 -© ENI Editions - All rigths reserved

Page 20: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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&eacute;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 - © ENI Editions - All rigths reserved

Page 21: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 22: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 http://tiles.apache.org. 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 - © ENI Editions - All rigths reserved

Page 23: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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="http://tiles.apache.org/tags-tiles" 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-8859-1" ?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN" "http://struts.apache.org/dtds/tiles-config_2_0.dtd"> <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 -© ENI Editions - All rigths reserved

Page 24: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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> <listener- class>org.apache.struts2.tiles.StrutsTilesListener</listener- class> </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="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <listener> <listener- class>org.apache.struts2.tiles.StrutsTilesListener</listener- class>

- 4 - © ENI Editions - All rigths reserved

Page 25: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</listener> <filter> <filter-name>struts2</filter-name> <filter- class>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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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="http://tiles.apache.org/tags-tiles" 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 -© ENI Editions - All rigths reserved

Page 26: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Utilisation du plug­in Tiles pour l’architecture des pages

- 6 - © ENI Editions - All rigths reserved

Page 27: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

É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 com.opensymphony.xwork2.ActionInvocation; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 28: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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" "http://struts.apache.org/dtds/struts-2.0.dtd">

- 2 - © ENI Editions - All rigths reserved

Page 29: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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">http://localhost</param> </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 -© ENI Editions - All rigths reserved

Page 30: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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">http://www.google.fr</param> <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 exemple36.modele.ClientModele; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 31: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple36.javabeans; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 32: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

- 3 -© ENI Editions - All rigths reserved

Page 33: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Les autres plug­ins

Le site http://cwiki.apache.org/S2PLUGINS/home.html 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.

- 1 -© ENI Editions - All rigths reserved

Page 34: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 35: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 36: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 37: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 http://localhost:8080/exemple37/client 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’annotation @Action permet de déclarer une URL et l’annotation @Actions 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 exemple37.modele.ClientModele; @SuppressWarnings("serial") public class ClientAction extends ActionSupport // liste des clients private List<Client> listeClients; // annotation du plug-in Convention @Action("/ListerClient") public String execute() System.out.println("Trace dans la méthode de liste"); listeClients=ClientModele.getListeClients(); return SUCCESS; public List<Client> getListeClients()

- 1 -© ENI Editions - All rigths reserved

Page 38: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 plug-in Convention @Actions( @Action(value="/", results=@Result(name="success", location="/WEB- INF/content/ListerClient.jsp") ), @Action(value="/ListerClient", 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’annotation @Results 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.

- 2 - © ENI Editions - All rigths reserved

Page 39: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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’annotation @InterceptorRef et le nom de l’intercepteur à utiliser avant la définition de la classe d’action.

Code : exemple38.actions.ClientAction.java ... @SuppressWarnings("serial") @InterceptorRef("paramsPrepareParamsStack") public class ClientAction extends ActionSupport implements Preparable ...

L’annotation @InterceptoRefs permet de déclarer plusieurs intercepteurs dans la même classe d’action.

@InterceptorRefs( @InterceptorRef("paramsPrepareParamsStack"), @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’annotation @InterceptorRef 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; import exemple38.modele.ClientModele; @SuppressWarnings("serial") @InterceptorRef("paramsPrepareParamsStack") public class ClientAction extends ActionSupport implements Preparable // liste des clients

- 3 -© ENI Editions - All rigths reserved

Page 40: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 plug-in Convention @Actions( @Action(value="/", results=@Result(name="success", location="/jsp/ListerClient.jsp") ), @Action(value="/ListerClient", results=@Result(name="success", location="/jsp/ListerClient.jsp") ) ) public String lister() listeClients=ClientModele.getListeClients(); return SUCCESS; // annotation du plug-in Convention @Action(value="/AjouterClient", results= @Result(name="success", location="/ListerClient", type="redirect"), @Result(name="input", 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 édition @Action(value="/EditerClient", results=@Result(name="success", location="/jsp/EditerClient.jsp") ) public String editer() return SUCCESS; // modifier un client @Action(value="/ModifierClient", results=

- 4 - © ENI Editions - All rigths reserved

Page 41: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

@Result(name="success", location="/ListerClient", type="redirect"), @Result(name="input", location="/jsp/EditerClient.jsp") ) public String modifier() ClientModele.modifier(client); return SUCCESS; // supprimer un client à partir du paramètre reçu nommé idClient @Action(value="/SupprimerClient", 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 -© ENI Editions - All rigths reserved

Page 42: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple38.actions; ... @Namespace("/GestionClient")

- 6 - © ENI Editions - All rigths reserved

Page 43: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

@SuppressWarnings("serial") @InterceptorRef("paramsPrepareParamsStack") public class ClientAction extends ActionSupport implements Preparable ...

L’application est désormais accessible à partir des URLs suivantes :

http://localhost:8080/exemple38/ListerClient.action

http://localhost:8080/exemple38/GestionClient/ListerClient.action

6. Annotations des vues

L’annotation @ResultPath 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 package exemple38.actions; ... @ResultPath("/jsp") @SuppressWarnings("serial") @InterceptorRef("paramsPrepareParamsStack") public class ClientAction extends ActionSupport implements Preparable ...

7. Annotations des exceptions

L’annotation @ExceptionMappings est définie au niveau de la déclaration de la classe d’action et contient des annotations @ExceptionMapping permettant de définir les types d’exceptions à gérer.

Code : exemple38.actions.ClientAction package exemple38.actions; ... @ExceptionMappings( @ExceptionMapping(exception = "java.lang.NullPointerException", result = "success", params = "parametre1", "valeurparametre1") ) @SuppressWarnings("serial") @InterceptorRef("paramsPrepareParamsStack") public class ClientAction extends ActionSupport implements Preparable ...

8. 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.

- 7 -© ENI Editions - All rigths reserved

Page 44: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 45: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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).

- 1 -© ENI Editions - All rigths reserved

Page 46: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 47: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 48: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 2 - © ENI Editions - All rigths reserved

Page 49: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (http://www.opensymphony.com et http://www.ognl.org/) 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.emailContat"/>

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 arobase @. 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&egrave;s au premier indice du tableau : <s:property value="listePays[0]"/><br/>

- 1 -© ENI Editions - All rigths reserved

Page 50: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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&egrave;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&egrave;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&eacute;clencher une m&eacute;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.

- 2 - © ENI Editions - All rigths reserved

Page 51: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Code : struts.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0// EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple39" namespace="/" extends="struts- default"> <default-action-ref name="OGNL" /> <action name="OGNL" class="exemple39.OgnlAction"> <result>/jsp/Ognl.jsp</result> </action> </package> </struts>

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 exemple39.javabeans.Client; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 52: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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&ecirc;te : <s:property value="client"/><br/> Lecture du client dans la requ&ecirc;te : <s:property value="client.identifiant"/><br/> Lecture du client dans la requ&ecirc;te : <s:property value="client[’identifiant’]"/><br/> Lecture du client dans la requ&ecirc;te : <s:property value="#request.client[’identifiant’]"/><br/> <hr/>

- 4 - © ENI Editions - All rigths reserved

Page 53: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.emailContat"/><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&egrave;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&egrave;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&egrave;s au premier indice du tableau : <s:property value="listeClients[0].getIdentifiant()"/><br/> Liste vide ? : <s:property value="listeClients.isEmpty"/><br/> <hr/> D&eacute;clencher une m&eacute;thode de classe d’action : <s:property value="%getAffichage()"/> <hr/> Liste cr&eacute;&eacute;e avec OGNL<br/> <s:select label="Label de la liste" name="nom" list="’valeur 1’,’valeur 2’,’valeur 3’"/> </div> </body> </html>

- 5 -© ENI Editions - All rigths reserved

Page 54: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Arborescence du projet exemple39

Manipulation des informations à l’aide d’OGNL

- 6 - © ENI Editions - All rigths reserved

Page 55: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 56: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 57: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 <servlet-name> 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 1.

- 2 - © ENI Editions - All rigths reserved

Page 58: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 java.io.Serializable; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 59: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 60: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 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 -© ENI Editions - All rigths reserved

Page 61: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 : http://localhost:8080/exemple01/Ajouter_client.action

Formulaire d’ajout client exemple01

Lorsque l’utilisateur valide la création d’un client, l’URL suivante est alors déclenchée : http://localhost:8080/exemple01/ValiderAjouter_client.action

Affichage des informations client exemple01

- 4 - © ENI Editions - All rigths reserved

Page 62: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 63: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class FiltreControleur implements Filter private FilterConfig filterConfig; public void init(FilterConfig filterConfig) throws ServletException this.filterConfig=filterConfig;

- 2 - © ENI Editions - All rigths reserved

Page 64: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

- 3 -© ENI Editions - All rigths reserved

Page 65: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 : http://localhost:8080/exemple01­2/Ajouter_client.action

Console et traces du filtre

- 4 - © ENI Editions - All rigths reserved

Page 66: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 2.

- 1 -© ENI Editions - All rigths reserved

Page 67: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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­core­version.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.

- 1 -© ENI Editions - All rigths reserved

Page 68: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <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 -© ENI Editions - All rigths reserved

Page 69: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 2 - © ENI Editions - All rigths reserved

Page 70: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 71: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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.

- 1 -© ENI Editions - All rigths reserved

Page 72: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 : http://localhost:8080/exemple01/admin

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> ... <package name="exemple01" namespace="/admin" extends="struts- default"> ... </package> ... </struts>

La balise <package/> doit dans la plupart des cas étendre le paquetage struts­default définit dans le fichier struts­default.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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="default" namespace="/" extends="struts- default"> <default-action-ref name="index" /> <action name="index"> <result type="redirectAction">

- 2 - © ENI Editions - All rigths reserved

Page 73: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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.

- 3 -© ENI Editions - All rigths reserved

Page 74: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 - © ENI Editions - All rigths reserved

Page 75: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 ci­dessous la liste des intercepteurs Struts utilisés par défaut :

<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/>

- 5 -© ENI Editions - All rigths reserved

Page 76: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 - © ENI Editions - All rigths reserved

Page 77: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 78: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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=2097152 : 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 -© ENI Editions - All rigths reserved

Page 79: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 2 - © ENI Editions - All rigths reserved

Page 80: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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-8859-1"?> <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 -© ENI Editions - All rigths reserved

Page 81: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 -© ENI Editions - All rigths reserved

Page 82: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <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 -© ENI Editions - All rigths reserved

Page 83: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple02" namespace="/" extends="struts- default"> <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 java.io.Serializable; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 84: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 : http://localhost:8080/exemple02/Ajouter_Client.action

- 3 -© ENI Editions - All rigths reserved

Page 85: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 86: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

- 5 -© ENI Editions - All rigths reserved

Page 87: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é.

- 1 -© ENI Editions - All rigths reserved

Page 88: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 java.io.Serializable; @SuppressWarnings("serial") 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 (http://logging.apache.org/) 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 -© ENI Editions - All rigths reserved

Page 89: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 90: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 91: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 : http://localhost:8080/exemple02/ValiderAjouter_Client.action?debug=xml

- 2 - © ENI Editions - All rigths reserved

Page 92: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 3 -© ENI Editions - All rigths reserved

Page 93: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Console de débogage OGNL

Cette console n’est actuellement pas opérationnelle avec le navigateur Internet Explorer mais fonctionne sous Firefox.

- 4 - © ENI Editions - All rigths reserved

Page 94: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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. http://localhost:8080/exemple02/ValiderAjouter_Client.action?profiling=yes

Pour stopper le profilage d’une application, il suffit de passer le paramètre profiling=no ou profiling=false http://localhost:8080/exemple02/ValiderAjouter_Client.action?profiling=no

2) Activer la méthode UtilTimerStack.setActive(true) dans la fonction à profiler.

Code : exemple02.Client.java package exemple02; import java.io.Serializable; import com.opensymphony.xwork2.util.profiling.UtilTimerStack; @SuppressWarnings("serial") 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 com.opensymphony.xwork2.util.profiling.UtilTimerStack; @SuppressWarnings("serial") 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 : http://localhost:8080/exemple02/ValiderAjouter_Client.action?profiling=true

INFO: [500ms] - Handling request from Dispatcher [0ms] - create DefaultActionProxy:

- 1 -© ENI Editions - All rigths reserved

Page 95: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

[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 com.opensymphony.xwork2.util.profiling.UtilTimerStack; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 96: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 97: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 98: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 99: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

@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 - © ENI Editions - All rigths reserved

Page 100: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.util.profiling.UtilTimerStack; @SuppressWarnings("serial") 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("email"));

- 1 -© ENI Editions - All rigths reserved

Page 101: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

return "success";

Code : /WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> ... <!-- Parametres globaux --> <context-param> <param-name>email</param-name> <param-value>[email protected]</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 - © ENI Editions - All rigths reserved

Page 102: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple03" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 103: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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é.

http://localhost:8080/exemple03/Ajouter_Client.action

- 4 - © ENI Editions - All rigths reserved

Page 104: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class Client extends ActionSupport implements SessionAware private String identifiant; private String motdepasse; private String page; ... public String getPage() return page;

- 5 -© ENI Editions - All rigths reserved

Page 105: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 106: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 http://localhost: 8080/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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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="*_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 http://localhost:8080/exemple04/Ajouter_Client.action va déclencher l’action nommée *_Client et la méthode Ajouter() de la classe exemple04.Client. De même, l’URL http://localhost:8080/exemple04/ValiderAjouter_Client.action 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.

- 1 -© ENI Editions - All rigths reserved

Page 107: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="backoffice" namespace="/admin" extends="struts- default"> <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 - © ENI Editions - All rigths reserved

Page 108: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 -© ENI Editions - All rigths reserved

Page 109: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 4 - © ENI Editions - All rigths reserved

Page 110: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class Client extends ActionSupport private String identifiant;

- 1 -© ENI Editions - All rigths reserved

Page 111: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 112: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire d’ajout avec invocation dynamique

Affichage du formulaire avec invocation dynamique

- 3 -© ENI Editions - All rigths reserved

Page 113: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 114: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 : www.gdawj.com).

<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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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> </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">http://www.google.fr</result> </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 - © ENI Editions - All rigths reserved

Page 115: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

@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 : http://localhost:8080/exemple04/Ajouter_Client.action

- 3 -© ENI Editions - All rigths reserved

Page 116: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 - © ENI Editions - All rigths reserved

Page 117: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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…).

- 5 -© ENI Editions - All rigths reserved

Page 118: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple04" namespace="/" extends="struts-

- 6 - © ENI Editions - All rigths reserved

Page 119: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 7 -© ENI Editions - All rigths reserved

Page 120: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple04" namespace="/" extends="struts-

- 8 - © ENI Editions - All rigths reserved

Page 121: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 122: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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">

- 1 -© ENI Editions - All rigths reserved

Page 123: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 <global-results/>. 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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" />

- 2 - © ENI Editions - All rigths reserved

Page 124: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<package name="exemple04" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 125: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 126: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é.

- 1 -© ENI Editions - All rigths reserved

Page 127: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 128: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 - © ENI Editions - All rigths reserved

Page 129: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 -© ENI Editions - All rigths reserved

Page 130: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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"> <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 - © ENI Editions - All rigths reserved

Page 131: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 -© ENI Editions - All rigths reserved

Page 132: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 - © ENI Editions - All rigths reserved

Page 133: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 134: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial")

- 8 - © ENI Editions - All rigths reserved

Page 135: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple05" namespace="/" extends="struts- default"> <default-action-ref name="Ajouter_Client" />

- 9 -© ENI Editions - All rigths reserved

Page 136: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 : http://localhost:8080/exemple05/Ajouter_Client.action

Code : exemple05.Client.java package exemple05; import java.util.ArrayList; import java.util.List; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class Client extends ActionSupport private String identifiant; private String motdepasse; private int profession; private List<Profession> listeProfessions=new ArrayList<Profession>(); public Client()

- 10 - © ENI Editions - All rigths reserved

Page 137: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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;

- 11 -© ENI Editions - All rigths reserved

Page 138: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 12 - © ENI Editions - All rigths reserved

Page 139: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Code : exemple05.Client.java package exemple05; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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

- 13 -© ENI Editions - All rigths reserved

Page 140: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é.

- 14 - © ENI Editions - All rigths reserved

Page 141: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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

- 15 -© ENI Editions - All rigths reserved

Page 142: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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>

- 16 - © ENI Editions - All rigths reserved

Page 143: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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;

- 17 -© ENI Editions - All rigths reserved

Page 144: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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>

- 18 - © ENI Editions - All rigths reserved

Page 145: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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).

- 19 -© ENI Editions - All rigths reserved

Page 146: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="emailWebmestre"/> <s:submit value="Ajouter le client"/> </s:form> </div> </body></html>

Utilisation de la balise <s:property/>

- 1 -© ENI Editions - All rigths reserved

Page 147: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

La notation JSP EL Expression Language peut également être utilisée avec le dollar et les accolades : $emailWebmestre.

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="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <context-param> <param-name>emailContat</param-name> <param-value>[email protected]</param-value> </context-param> <filter> <filter-name>struts2</filter-name> <filter- class>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"/> Email webmestre : <s:property value="emailWebmestre"/><br/> Email webmestre : $emailWebmestre<br/> Email contact : <s:property value="#application.emailContat"/><br/> <s:submit value="Ajouter le client"/> </s:form> </div> </body> </html>

- 2 - © ENI Editions - All rigths reserved

Page 148: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 149: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Email webmestre : <s:property value="emailWebmestre"/><br/> Email webmestre : $emailWebmestre<br/> Email contact : <s:property value="#application.emailContat"/><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&egrave;tre &agrave; 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 - © ENI Editions - All rigths reserved

Page 150: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 151: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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&egrave;tre &agrave; 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 - © ENI Editions - All rigths reserved

Page 152: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class Client extends ActionSupport private String identifiant; private String motdepasse; private String emailWebmestre="[email protected]"; 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 -© ENI Editions - All rigths reserved

Page 153: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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 - © ENI Editions - All rigths reserved

Page 154: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 155: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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">

- 10 - © ENI Editions - All rigths reserved

Page 156: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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;

- 11 -© ENI Editions - All rigths reserved

Page 157: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class Client extends ActionSupport private String identifiant; private String motdepasse; private String emailWebmestre="[email protected]"; 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"

- 12 - © ENI Editions - All rigths reserved

Page 158: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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

- 13 -© ENI Editions - All rigths reserved

Page 159: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 160: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 161: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é.

- 1 -© ENI Editions - All rigths reserved

Page 162: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 : http://localhost:8080/exemple07/Ajouter_Client.action

Arborescence exemple07

Code : struts.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts>

- 1 -© ENI Editions - All rigths reserved

Page 163: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 164: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 165: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 - © ENI Editions - All rigths reserved

Page 166: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 167: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 168: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 169: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 170: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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 -© ENI Editions - All rigths reserved

Page 171: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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&ccedil;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&ccedil;ais</a></li><li><a href="Ajouter_Client.action?request_locale=en">Anglais</a></li></ul>

- 1 -© ENI Editions - All rigths reserved

Page 172: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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

- 2 - © ENI Editions - All rigths reserved

Page 173: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 174: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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:property value="message"/> </p> </div> </body> </html>

Affichage des informations client avec accès aux messages

- 2 - © ENI Editions - All rigths reserved

Page 175: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 176: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 177: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (NomClasseAction­

validation.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 ClientAction­ajoutermodifier_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.

- 1 -© ENI Editions - All rigths reserved

Page 178: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators>

- 2 - © ENI Editions - All rigths reserved

Page 179: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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.

- 3 -© ENI Editions - All rigths reserved

Page 180: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 181: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

#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 -© ENI Editions - All rigths reserved

Page 182: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <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 -© ENI Editions - All rigths reserved

Page 183: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 2000.

<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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class ClientAction extends ActionSupport

- 2 - © ENI Editions - All rigths reserved

Page 184: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

private String identifiant; private String motdepasse; private String email; // 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="email" id="email" label="Email" labelposition="top" cssClass="input"/> <s:submit value="Ajouter un client"/> </s:form> </div> </body> </html>

- 3 -© ENI Editions - All rigths reserved

Page 185: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 186: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

@SuppressWarnings("serial") public class ClientAction extends ActionSupport private String identifiant; private String motdepasse; private String email; 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" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <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="email"> <field-validator type="email"> <message>Le champ email 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 -© ENI Editions - All rigths reserved

Page 187: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 188: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <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 -© ENI Editions - All rigths reserved

Page 189: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 190: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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

- 9 -© ENI Editions - All rigths reserved

Page 191: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple09" namespace="/" extends="struts-default">

- 1 -© ENI Editions - All rigths reserved

Page 192: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 exemple09.javabeans.Profession; @SuppressWarnings("serial") 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 exemple09.javabeans; @SuppressWarnings("serial") public class Client

- 2 - © ENI Editions - All rigths reserved

Page 193: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

private String identifiant; private String motdepasse; private Profession profession; public Client() // getter et setter

Code : exemple09.Profession.java package exemple09.javabeans; @SuppressWarnings("serial") 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&ccedil;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 -© ENI Editions - All rigths reserved

Page 194: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 - © ENI Editions - All rigths reserved

Page 195: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 196: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

import exemple09.javabeans.Client; import exemple09.javabeans.Profession; @SuppressWarnings("serial") 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&ccedil;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 - © ENI Editions - All rigths reserved

Page 197: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 -© ENI Editions - All rigths reserved

Page 198: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 199: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire client et thème simple

- 5 -© ENI Editions - All rigths reserved

Page 200: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

É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" "http://www.opensymphony.com/xwork/xwork-validator-config- 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 -© ENI Editions - All rigths reserved

Page 201: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator "/> <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" "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd"> <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.

- 2 - © ENI Editions - All rigths reserved

Page 202: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Arborescence du projet exemple10

Code : /exemple10/ClientAction-ValiderAjouter_Client-validation.xml <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <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 -© ENI Editions - All rigths reserved

Page 203: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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); // 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 caractères 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"); if(listeIdentifiants.contains(identifiant)) System.out.println(identifiant+" est dans la liste"); return true; System.out.println(identifiant+" n’est pas dans la liste"); return false;

- 4 - © ENI Editions - All rigths reserved

Page 204: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Code : exemple10.ClientAction.java package exemple10; import java.util.ArrayList; import java.util.List; import com.opensymphony.xwork2.ActionSupport; import exemple10.javabeans.Client; import exemple10.javabeans.Profession; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 205: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 206: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple10.javabeans.Profession; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 207: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire client avec validation personnelle de l’identifiant

- 8 - © ENI Editions - All rigths reserved

Page 208: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 209: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 210: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 211: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 2 - © ENI Editions - All rigths reserved

Page 212: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é.

- 1 -© ENI Editions - All rigths reserved

Page 213: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 ClientAction­conversion.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 -© ENI Editions - All rigths reserved

Page 214: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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&ccedil;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 - © ENI Editions - All rigths reserved

Page 215: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 216: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire d’ajout client exemple12

Affichage du compte client

- 4 - © ENI Editions - All rigths reserved

Page 217: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 218: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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).

- 1 -© ENI Editions - All rigths reserved

Page 219: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple13" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 220: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 exemple13.modele.ClientModele; @SuppressWarnings("serial") 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 exemple13.javabeans; @SuppressWarnings("serial") public class Client private int idClient; private String identifiant; private String motdepasse; public Client()

- 2 - © ENI Editions - All rigths reserved

Page 221: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 http://localhost:8080/exemple13/Lister_Client.action 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.

- 3 -© ENI Editions - All rigths reserved

Page 222: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 4 - © ENI Editions - All rigths reserved

Page 223: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple14" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 224: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 exemple14.modele.ClientModele; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 225: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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 -© ENI Editions - All rigths reserved

Page 226: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 227: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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 -© ENI Editions - All rigths reserved

Page 228: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Editer 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>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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <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.

- 6 - © ENI Editions - All rigths reserved

Page 229: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Gestion complète des clients

- 7 -© ENI Editions - All rigths reserved

Page 230: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 231: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 232: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- Chargeur du datasource --> <listener> <listener- class>exemple15.boiteoutils.ApplicationListener</listener-class> </listener> <!-- Parametre globaux --> <context-param> <param-name>dataSourceJNDI</param-name> <param-value>java:/comp/env/jdbc_struts2_MySQL</param- value> </context-param> <filter> <filter-name>struts2</filter-name> <filter- class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExec uteFilter</filter-class>

- 3 -© ENI Editions - All rigths reserved

Page 233: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 - © ENI Editions - All rigths reserved

Page 234: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

if(context!=null) context.close(); catch(Exception e) System.out.println("Erreur lors de initCtx !"); //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 -© ENI Editions - All rigths reserved

Page 235: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 mysql­

connector­java­3.1.11­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 - © ENI Editions - All rigths reserved

Page 236: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple15.modeles.ModeleClientDAO; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 237: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 238: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 // Ouverture d’une connexion connexion=super.getConnection(); // requete pour la liste des clients requeteString="SELECT * FROM client WHERE 1 ORDER BY idClient"; 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); // On ajoute l’objet a la liste des clients listeClients.add((Client)client); catch(Exception e)

- 9 -© ENI Editions - All rigths reserved

Page 239: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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(); // Création de la requête requeteString = "SELECT * FROM client WHERE idClient=?"; // 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);

- 10 - © ENI Editions - All rigths reserved

Page 240: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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;

- 11 -© ENI Editions - All rigths reserved

Page 241: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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(); // Supprimer le client requeteString="DELETE FROM client WHERE idClient=?"; requete=connexion.prepareStatement(requeteString); requete.setInt(1, idClient); // Execution de la requête codeErreur=requete.executeUpdate(); catch(Exception e) codeErreur=0;

- 12 - © ENI Editions - All rigths reserved

Page 242: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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

- 13 -© ENI Editions - All rigths reserved

Page 243: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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

- 14 - © ENI Editions - All rigths reserved

Page 244: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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;

- 15 -© ENI Editions - All rigths reserved

Page 245: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 246: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 247: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 248: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple16" namespace="/" extends="struts- default"> <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.

- 1 -© ENI Editions - All rigths reserved

Page 249: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple16.javabeans; @SuppressWarnings("serial") 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 exemple16.modele.ClientModele; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 250: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 251: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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>&nbsp;</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.

- 3 -© ENI Editions - All rigths reserved

Page 252: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple16" namespace="/" extends="struts- default"> <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 - © ENI Editions - All rigths reserved

Page 253: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é struts­messages.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 -© ENI Editions - All rigths reserved

Page 254: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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=102400 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 struts­message.properties que de langues en les suffixant par la locale (ex : struts­message_en.properties).

- 6 - © ENI Editions - All rigths reserved

Page 255: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Gestion des messages d’erreurs pour la langue

- 7 -© ENI Editions - All rigths reserved

Page 256: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 conséquence.

@SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 257: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 http://www.davidjc.com/ajaxfileupload/demo!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="http://www.davidjc.com/taglibs" 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="http://www.davidjc.com/taglibs" 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="progress- bgrd">&nbsp;</div></div> <div id="progress-text">&nbsp;</div> <br/>

- 1 -© ENI Editions - All rigths reserved

Page 258: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple17" namespace="/" extends="struts- default"> <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 com.opensymphony.xwork2.Action; @SuppressWarnings("serial") 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 - © ENI Editions - All rigths reserved

Page 259: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 260: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Chargement asynchrone de fichiers en Ajax

- 4 - © ENI Editions - All rigths reserved

Page 261: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 262: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 263: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple18" namespace="/" extends="struts- default"> <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-

- 1 -© ENI Editions - All rigths reserved

Page 264: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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&eacute;l&eacute;charger une image</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>T&eacute;l&eacute;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&eacute;l&eacute;charger l’image </a> <br/> <img src="Afficher_Image.action? cheminFichier=/imagesclients/image.png" align="bottom"/> </div>

- 2 - © ENI Editions - All rigths reserved

Page 265: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</body> </html>

Arborescence du projet exemple18

Téléchargement d’un fichier sous la forme d’octets

- 3 -© ENI Editions - All rigths reserved

Page 266: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple19" namespace="/" extends="struts- default"> <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 com.opensymphony.xwork2.ActionContext; @SuppressWarnings("serial") public class TelechargerAction extends ActionSupport implements

- 1 -© ENI Editions - All rigths reserved

Page 267: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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/octet- stream) 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/octet- stream"); 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 - © ENI Editions - All rigths reserved

Page 268: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<title>T&eacute;l&eacute;charger une image</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>T&eacute;l&eacute;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&eacute;l&eacute;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&eacute;l&eacute;charger : TelechargerAction.java </a> </div> </body> </html>

Affichage et téléchargement dynamique de fichiers

- 3 -© ENI Editions - All rigths reserved

Page 269: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

- 4 - © ENI Editions - All rigths reserved

Page 270: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 271: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 272: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple20" namespace="/" extends="struts- default"> <default-action-ref name="Chargeur" /> <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> </package> </struts>

Code : exemple20.Chargeur.action package exemple20; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 273: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Chargeur</title> <meta http-equiv="refresh" content="1;url=/exemple20/Chargeur.action"/> <style type="text/css">@import 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 - © ENI Editions - All rigths reserved

Page 274: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

@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&eacute; </div> </body> </html>

Chargement de fichier et progression

- 3 -© ENI Editions - All rigths reserved

Page 275: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é.

- 1 -© ENI Editions - All rigths reserved

Page 276: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 277: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

É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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple21" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 278: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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 com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") 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"/> ...

- 2 - © ENI Editions - All rigths reserved

Page 279: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</action>

Code : exemple22.AuthentificationIntercepteur.java package exemple22; import java.util.Map; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; @SuppressWarnings("serial") 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 -© ENI Editions - All rigths reserved

Page 280: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<html> <head> <title>Liste</title> <style type="text/css">@import url(css/styles.css);</style> </head> <body> <div id="enveloppe"> <h3>Liste</h3> <a href="Proteger.action">Acc&eacute;der &agrave; 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&eacute;g&eacute;e, vous &ecirc;tes authentifi&eacute; <br/><a href="Deconnecter.action">Se deconnecter</a> </div> </body> </html>

- 4 - © ENI Editions - All rigths reserved

Page 281: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Formulaire d’authentification client

Accès protégé par intercepteur Struts personnalisé

- 5 -© ENI Editions - All rigths reserved

Page 282: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 283: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 284: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

É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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple23" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 285: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple23.javabeans.Client; @SuppressWarnings("serial") 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"); // 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 clients</title>"); out.println("<link>http://localhost:8080/exemple23/</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>http://localhost:8080/exemple23/Editer_Client. action?idClientEnCours="+client.getIdClient()+"</link>"); out.println("<description> Client : "+client.getIdClient()+" - "+client.getIdentifiant()+" -

- 2 - © ENI Editions - All rigths reserved

Page 286: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

"+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 -© ENI Editions - All rigths reserved

Page 287: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Affichage du résultat au format RSS

- 4 - © ENI Editions - All rigths reserved

Page 288: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 289: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 290: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple24.modele.ClientModele; @SuppressWarnings("serial") 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.

- 1 -© ENI Editions - All rigths reserved

Page 291: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 292: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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.

- 3 -© ENI Editions - All rigths reserved

Page 293: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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é struts­messages.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é.

- 4 - © ENI Editions - All rigths reserved

Page 294: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 5 -© ENI Editions - All rigths reserved

Page 295: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 296: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 297: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (http://www.prototypejs.org/) ainsi que Scriptaculous (http://script.aculo.us/) 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 (http://extjs.com/). Enfin, la plus utilisée et préconisée par les concepteurs Struts est JQuery (http://jquery.com/). 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>).

- 1 -© ENI Editions - All rigths reserved

Page 298: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (http://malsup.com/jquery/form/) 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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple25" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 299: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 300: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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.

- 3 -© ENI Editions - All rigths reserved

Page 301: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 302: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

(’#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 -© ENI Editions - All rigths reserved

Page 303: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 304: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Arborescence du projet exemple25

Gestion des clients en Ajax

- 7 -© ENI Editions - All rigths reserved

Page 305: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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("background- image","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("background- image","url(./images/valider.png)");

- 1 -© ENI Editions - All rigths reserved

Page 306: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

$("#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 (http://jqueryui.com/). 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-1.7.1.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-1.7.1.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 - © ENI Editions - All rigths reserved

Page 307: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 308: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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); ...

3. 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.

- 4 - © ENI Editions - All rigths reserved

Page 309: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 widget-editbox 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 -© ENI Editions - All rigths reserved

Page 310: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<script src="javascript/exemple26.js" type="text/javascript"></script> <!-- Utiliser la librairie JavaScript JQueryUI --> <style type="text/css">@import url(javascript/plugin/jqueryui/css/ smoothness/jquery-ui-1.7.1.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-1.7.1.custom.min.js"></script> <script type="text/javascript" src="javascript/exemplejqueryui.js"></script> <!-- Utiliser la librairie JavaScript easywidgets --> <style type="text/css">@import url(javascript/plugin/easywidgets/css/jquery.easywidgets.css);</st yle> <style type="text/css">@import 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&eacute;r&eacute;e en Ajax. </div> </div> </div> <!-- boite utilisée pour les confirmations --> <div id="BoiteConfirmation" title="Gestion des clients"

- 6 - © ENI Editions - All rigths reserved

Page 311: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 7 -© ENI Editions - All rigths reserved

Page 312: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple27.javabeans; @SuppressWarnings("serial") public class Client private int idClient; private String identifiant; private String motdepasse; private String datedenaissance; private String description; public Client()

- 8 - © ENI Editions - All rigths reserved

Page 313: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 314: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (http://jqueryui.com/demos/datepicker/#default). 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

- 10 - © ENI Editions - All rigths reserved

Page 315: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

); // 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

- 11 -© ENI Editions - All rigths reserved

Page 316: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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();

- 12 - © ENI Editions - All rigths reserved

Page 317: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

); </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-8859- 1" pageEncoding="ISO-8859-1"%> <%@ 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="";

- 13 -© ENI Editions - All rigths reserved

Page 318: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

//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"));

- 14 - © ENI Editions - All rigths reserved

Page 319: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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);

- 15 -© ENI Editions - All rigths reserved

Page 320: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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());

- 16 - © ENI Editions - All rigths reserved

Page 321: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

// 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 exemple27

- 17 -© ENI Editions - All rigths reserved

Page 322: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (http://tablesorter.com/docs/) 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>

- 18 - © ENI Editions - All rigths reserved

Page 323: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</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>

- 19 -© ENI Editions - All rigths reserved

Page 324: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Utilisation du plug­in JavaScript de gestion des tris

- 20 - © ENI Editions - All rigths reserved

Page 325: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 326: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 (http://velocity.apache.org/) 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.

- 1 -© ENI Editions - All rigths reserved

Page 327: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple28" namespace="/" extends="struts- default">

- 1 -© ENI Editions - All rigths reserved

Page 328: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

<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 - © ENI Editions - All rigths reserved

Page 329: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 330: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Gestion des clients avec le moteur de templates Velocity

- 4 - © ENI Editions - All rigths reserved

Page 331: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

FreeMarker

FreeMarker (http://freemarker.org/) 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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple29" namespace="/" extends="struts- default"> <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 -© ENI Editions - All rigths reserved

Page 332: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 - © ENI Editions - All rigths reserved

Page 333: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

</body> </html>

Arborescence du projet exemple29

- 3 -© ENI Editions - All rigths reserved

Page 334: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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.

- 1 -© ENI Editions - All rigths reserved

Page 335: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 -© ENI Editions - All rigths reserved

Page 336: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="exemple30" namespace="/" extends="struts- default"> <default-action-ref name="Ajouter_Client" /> <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> </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="XMLXSL" id="Formulaire_Client" name="Formulaire_Client"> <s:textfield name="client.identifiant" id="client.identifiant" label="Identifiant" labelposition="top"

- 1 -© ENI Editions - All rigths reserved

Page 337: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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 exemple30.javabeans.Client; @SuppressWarnings("serial") 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="http://www.w3.org/1999/XSL/Transform"> <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 - © ENI Editions - All rigths reserved

Page 338: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Arborescence du projet exemple30

Formulaire d’ajout d’un compte client

- 3 -© ENI Editions - All rigths reserved

Page 339: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <rss version="2.0"> <channel> <title>Flux RSS des clients</title> <link>http://localhost:8080/exemple31/</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 - © ENI Editions - All rigths reserved

Page 340: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

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="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes" encoding="ISO-8859-1"/> <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 -© ENI Editions - All rigths reserved

Page 341: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

Affichage du compte client avec une feuille XSLT

- 6 - © ENI Editions - All rigths reserved

Page 342: Struts 2 Le framework de développement d'applications Java … · livre Java EE - Guide de ... Les applications peuvent être écrites de manière plus efficace si ... Introduction

En résumé

Struts fournit un moyen simple et efficace de gestion des résultats de type XSLT. Les réponses sont alors envoyées au format XML en association avec une feuille de style XSL. Cette architecture est la plus adaptée pour la gestion des sorties standards sous différents formats comme XML, RSS, XHTML ou encore PDF sans toucher au code et en manipulant uniquement la couche Vue du modèle MVC.

- 1 -© ENI Editions - All rigths reserved