Struts et le Web 2.0
Un article de ODcWiki.
| |
Dans cet article nous verrons comment mettre une pincée de Web 2.0 dans une application basée sur Struts. La plupart des outils et méthodes présentés ici s'appliquent aussi à des applications JSP/Servlet classiques ou basées sur d'autres framework orientés requête comme Stripes, Spring MVC, Beehive...
On dispose pour cela de deux grandes catégories d'outils :
- Les bibliothèques JavaScript comme Prototype, jQuery, Dojo, JS-Ext...
- Les bibliothèques de balises Java (taglib) comme Ajax Tags, Struts2 Dojo Plugin, Ajax Web Parts, JS Tags...
Sommaire |
Ajax (Rappels)
L'idée d'Ajax est de pouvoir appeler le serveur sans télécharger une nouvelle page complète. Cela fonctionne de la manière suivante :
- Côté navigateur : une page web est chargée de manière classique. Elle contient du code JavaScript qui va émettre une requête, dite Ajax, grâce à l'objet XML HTTP Request.
- Côté serveur : une action Struts/Stripes ou une Servlet réceptionne la requête HTTP et y répond. Plusieurs formats de réponses sont envisageables :
- Des données aux formats XML (le X de Ajax) que le navigateur devra parser, puis traiter en JavaScript;
- Un fragment de page HTML que le navigateur intègrera à la page complète;
- Des données au format JSON (JavaScript Object Notation) que le navigateur convertira en objets JavaScript puis traitera, toujours en JavaScript.
- Côté navigateur : la réponse est réceptionnée puis traitée.
L'émission de la requête Ajax (1) et la réception de la réponse (3) sont facilitées par l'utilisation de librairies JavaScript comme Prototype, jQuery, Dojo...
Le traitement côté serveur (2) utilise les mécanismes standards des applications Java/JSP/Servlet. La fabrication de la réponse s'appuie sur des librairies Java qui varient en fonction du format :
- XML : transformation XSLT (Xalan ou Saxon) ou mapping objet/XML (JAXB, Castor XML, Xtream, XMLBeans...);
- HTML (fragment) : JSPs habituelles et taglibs;
- JSON : JSON Java, JSON Lib, JSON Tools ou Struts2 JSON Plugin.
Au final, la plus grosse difficulté réside dans le traitement de la réponse dans le navigateur (3), car elle va nécessiter l'écriture de code JavaScript pour lire la réponse et modifier la page déjà affichée. Les problèmes des développements JavaScript risquent alors de survenir: débogage et maintenance ardue, portabilité inter-navigateur à surveiller... L'utilisation de réponses au format HTML, même si elle n'est pas toujours possible, limite le problème en laissant le plus gros du travail (la mise en forme de données) au serveur.
Remplacement dynamique d'une zone
Dans cette première partie, nous allons voir comment changer une zone de la page sans recharger totalement la page. D'un point de vue ergonomique, cela permet de rafraichir une portion de la page web, sans pour autant recharger la totalité de la page et avoir une page blanche le temps du chargement.
Pour cela, le navigateur va demander au serveur de lui fournir un fragment de page HTML (un paragraphe isolé dans une JSP indépendante). Par la suite, il va l'insérer ou remplacer ce paragraphe dans la page.
Dans un formulaire on va pouvoir, par exemple, lier le contenu d'une liste, au choix effectué dans une autre (combo box dépendantes). Ou encore, utilisé avec un timeout, on va pouvoir rafraichir une zone à intervalle régulier (par polling) et donner l'impression d'une mise à jour en temps réel.
JQuery
JQuery est une bibliothèque JavaScript qui traite et simplifie les problèmes que peut poser Ajax.
<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <c:url var="jQueryUrl" value="js/jquery-1.2.3.js"/> <script type="text/javascript" src="${jQueryUrl}"></script> <s:url id="refreshAction" action="maZone" /> <script type="text/javascript"> $(document).ready(function() { // 1 $("#refreshButton").click(function(e){ // 2 $.post("${refreshAction}", {}, function(html) { // 3 $("#refreshZone").html(html); // 4 }); }); }); </script> </head> <body> <div id="refreshButton">Rafraichir</div> <div id="refreshZone">???</div> </body> </html>
Les quelques lignes de code JavaScript signifient: lorsque le document est chargé (1), on écoute les clics sur l'élément refreshButton (2). En cas de clic, on émet une requête Ajax (3) vers l'URL pointée par la variable refreshAction. Enfin, lorsque la réponse arrive (4), on affecte à l'élément refreshZone le contenu HTML reçu, la zone est alors modifiée.
Ajax Tags
Même si le code JavaScript ci-dessus n'est pas très compliqué, il est possible de le remplacer par l'utilisation d'une taglib comme Ajax Tags.
<%@ taglib prefix="s" uri="/struts-tags" %> <%@ taglib prefix="ajax" uri="http://ajaxtags.org/tags/ajax"%> <html> <head> </head> <body> <div id="refreshButton">Rafraichir</div> <div id="refreshZone">???</div> <s:url id="refreshAction" action="maZone" /> <ajax:htmlContent baseUrl="${refreshAction}" source="refreshButton" target="refreshZone" parameters="param=value" /> </body> </html>
De la même manière que précédemment, on a :
- refreshButton: le composant qui va déclencher le rafraichissement de la zone (un bouton Rafraichir") : la source;
- refreshZone: la zone qui va être rafraichie (un Div) : la cible ou target;
- refreshAction le composant serveur qui va effectuer le rendu de la zone (ici action Struts + JSP) : baseUrl et parameters.
Puis on lie les 3, au moyen d'une balise <ajax:htmlContent /> définie dans la taglib Ajax Tags. Et c'est tout !
Struts2 Dojo Plugin
Ce plug-in pour Struts 2 suit le même principe qu'Ajax Tags : proposer des composants ajaxifiés enveloppés dans une taglib. Une partie des composants JavaScript Dojo sont ainsi utilisables au travers de balises Java.
<%@ taglib prefix="s" uri="/struts-tags" %> <%@ taglib prefix="sd" uri="/struts-dojo-tags" %> <html> <head> <sd:head/> </head> <body> <s:url id="refreshAction" action="maZone" /> <sd:div href="%{refreshAction}" showLoadingText="true" updateFreq="30000"></sd:div> </body> </html>
En quelques lignes on crée une zone qui va sa rafraîchir toute seule moyennant un polling toutes les 30 secondes. L'action refreshAction et la JSP associée produisent, côté serveur, le contenu de cette zone à chaque appel.
Composants graphiques évolués
Struts2 Dojo Plugin & Dojo Date Picker
Le plugin Dojo que nous nous venons d'utiliser contient aussi des composants DHTML comme un champ qui suggère des valeurs en fonction de la valeur saisie (autocomplete, suggest box) ou encore un champ pour saisir une date au moyen d'un calendrier (datepicker). Ils s'intègrent d'eux-mêmes au mécanisme de soumission de formulaire.
Utiliser un calendrier pour choisir une date dans un formulaire devient alors un jeu d'enfant :
<%@ taglib prefix="s" uri="/struts-tags" %> <%@ taglib prefix="sd" uri="/struts-dojo-tags" %> <html> <head> ... <sd:head/> </head> <body> ... <s:form action="creerPersonne"> <s:textfield name="personne.nom" label="Nom"/> <s:textfield name="personne.prenom" label="Prénom"/> <sd:datetimepicker name="personne.dateNaissance" label="Date Naissance" /> <s:submit/> </s:form> </body> </html>
Struts2 JSON Plugin & Dojo Grid
Dans ce paragraphe, nous verrons comment afficher des données dans un composant table évolué (tri côté client, déplacement des colonnes...). Nous utiliserons pour cela le composant graphique Dojo Grid alimenté par des données produites par une action Struts et le plug-in JSON.
Ce plug-in permet de sérialiser en JSON le contenu de l'action. Par exemple, en invoquant l'action suivante :
@ParentPackage(value="json-default") @Result(name=ActionSupport.SUCCESS,type=JSONResult.class,value="") public class ListerPersonneAction extends ActionSupport { private List<Personne> personnes; public List<Personne> getPersonnes() { return personnes; } @Override public String execute() throws Exception { personnes=PersonneService.getInstance().listerPersonnes(); return SUCCESS; } }
On obtient instantanément une sortie au format JSON du genre :
{"personnes":[
{"id":1,"nom":"Terieur","prenom":"Alain","dateNaissance":"2001-01-01"},
{"id":2,"nom":"Terieur","prenom":"Alex", "dateNaissance":"2002-02-02"}
]}
Ce plug-in est aussi capable de fonctionner dans le sens inverse, c'est-à-dire peupler une action à partir d'un message JSON. Bref, il est relativement aisé de mettre en place un mécanisme de RPC s'appuyant sur JSON et HTTP en utilisant Struts2 et son plug-in JSON. On peut aussi voir cette extension comme outil de mapping Objet Java / Objet JavaScript qui permet de faire transiter les données d'un monde à l'autre.
A présent, nous avons récupéré les données dans le navigateur, il ne reste plus qu'à les mettre en forme. Pour cela, nous allons utiliser le composant graphique Grid de la bibliothèque JavaScript Dojo. Malheureusement, le plug-in Dojo n'intègre pas ce composant (bien que ce soit envisageable). Qu'à cela ne tienne, nous allons attaquer Dojo directement en JavaScript et voir que quelques lignes suffisent à accomplir la tâche :<%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> ... </head> <body> <div id="personneGridDiv"></div> <script type="text/javascript"> dojo.require("dojox.data.QueryReadStore"); dojo.require("dojox.grid.Grid"); dojo.require("dojox.grid._data.model"); var personneStore = new dojox.data.QueryReadStore( {url: "<s:url namespace="/json" action="listerPersonne" />"}); // 1 var personneModel = new dojox.grid.data.DojoData(null, personneStore, {query:{ id :"*" },rowsPerPage:10,clientSort:true}); // 2 var personneGrid=new dojox.Grid( { model:personneModel, structure:[{cells: [[ {name: "Nom", field: "nom",width:"200px"}, {name: "Prénom", field: "prenom"}, {name: "Date Naissance", field: "dateNaissance"} ]]}], title:"Personnes", viewWidth:"300px" }, dojo.byId("personneGridDiv")); //3 </script> </body> </html>
Ce composant est découpé en suivant un découpage MVC :
- Un Modèle (dojox.data.QueryReadStore et dojox.grid.data.DojoData) qui se branche sur l'action Struts pour obtenir des données au format JSON;
- Une Vue (dojox.Grid) remplit l'élément Div et se charge la mise en page de données. On a pour cela la liste des colonnes et leur formatage.
Les deux premières lignes de JavaScript préparent le modèle de données qui sera alimenté directement par l'action Struts2. Quant à la dernière ligne, elle décrit en configurant les colonnes de la table (entête, largeur...).



