AJAX et Struts par l’exemple


6 01 2006

Premier vrai et long article technique de ce blog…

Préambule

On trouve aujourd’hui beaucoup de références sur le net sur AJAX ( XmlHTTPRequest en réalité ) mais peu d’éléments sur l’intégration de AJAX sur le framework Struts. Concernant les ressources francaises sur le sujet, c’est quasi inexistant en recherche sur Google. Je me suis donc décidé à écrire ce petit article technique. Attention, cet article n’explique pas réellement le fonctionnement de la technologie AJAX mais plutôt de son intégration avec le framework Struts.

Concept

Pour rappeler très briévement la technologie AJAX, il s’agit de permettre au navigateur d’effectuer des requêtes HTTP sur le serveur en Javascript et de mettre à jour dynamiquement le contenu de la page (via DHTML) sans recharger toute la fenêtre.

Rappel des étapes :

  1. Création d’un objet XmlHttpRequest (sous Javascript)
  2. Paramétrage de cet objet (url et méthode de callback)
  3. Exécution de la requête
  4. Coté serveur, on reçoit la requête que l’on traite puis on renvoie le résultat
  5. Le retour est traité dans la méthode callback préalablement définie

Pour plus d’informations, il existe de nombreux sites en parlant mieux que moi

Intégration avec Struts

Comme on peut le voir dans les étapes ci-dessus, l’intégration avec Struts ne se situe que lors de l’étape 4 puisque c’est la seule étape qui fait intervenir le serveur. Afin d’implémenter la réception des appels AJAX sur le serveur, plusieurs solutions sont possibles :

  • Ecrire des servlets spécifiques
  • Utiliser un framework comme DWR
  • Ecrire des actions Struts « standard » dédiés

La 1ère solution étant à proscrire et la deuxième impliquant l’utilisation d’un framework tiers, la solution d’actions dédiées est parfaitement adaptée pour des petites extensions sur des projets existants comme cela est souvent le cas avec l’utilisation de AJAX.

Mon exemple sera l’implémentation d’une page donnant l’heure du serveur. C’est un exemple trivial mais il permet de mettre en place la chaîne complète, ce qui est l’intérêt de cet article.

Etape 1 : Ecriture de la page HTML

On commence par la page HTML :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Test AJAX et Struts</title>
<script type="text/javascript">
<!- -  Ici on placera le code Javascript -->
</script>
</head>
<body>
<div id="form">
<form action="#">
<input type="button" name="Envoyer" onclick="appelAjaxExemple();" value="Je veux l'heure du serveur" />
 </form>
</div>
<hr />
<div id="resultat"> </div>
</body>
</html>

La page est la plus simple possible avec un simple bouton qui appelle la méthode Javascript et un DIV de résultat.
Ensuite on écrit le Javascript suivant entre les balises head :

// appelé dans la page function appelAjaxExemple() 
{ 	
ajaxCallRemotePage('ajaxAction.do'); 
}

Cette méthode est celle appelée par le bouton du formulaire. Il se contente d’appeler la méthode de traitement avec en paramètre l’action Struts qui traitera l’appel Asynchrone (voir plus bas)

 // Methode d'appel a une page en XmlHttpRequest 
function ajaxCallRemotePage(url) 
{     
if (window.XMLHttpRequest) 
{ 
// Non-IE browsers       	
req = new XMLHttpRequest();        	
req.onreadystatechange = processStateChange;         
req.open("GET", url, true);         
req.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"); 		
req.send(null); 	
} 	
else if (window.ActiveXObject) 
{ 
// IE       	
req = new ActiveXObject("Microsoft.XMLHTTP");        	
req.onreadystatechange = processStateChange;        	
req.open("GET", url, true);        	
req.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT"); 		
req.send(); 	
} 	
else { 		
return; // Navigateur non compatible 	
}  
}

Il s’agit de l’implémentation technique de la couche XmlHTTPRequest, on peut voir que le code est écrit pour gérer les navigateurs IE et les autres (dont Firefox). La requête sur le serveur est effectuée au moment du send(). On peut voir aussi que la méthode de callback processStateChange est défini. C’est la méthode qui est appelée lorsque l’état de la requête bouge. Voici son code :

 // Methode private qui traite le retour de l'appel de "ajaxCallRemotePage" 
function processStateChange() 
{     
if (req.readyState == 4) 
{ // Complete       
if (req.status == 200) 
{ // OK response        
  // Pour le debug        
  alert("ok:"+req.responseText);    
   } else {         
     // Pour le debug          
     alert("Problem: " + req.statusText);     
     alert("status: " + req.status);   
   }  
} }

L’algorithme est relativement simple. Si l’état de la requête est à 4 (terminée), on vérifie que le serveur nous a renvoyé un code HTTP de 200 qui correspond à un OK serveur. Alors dans ce cas, on affiche le résultat retourné par le serveur sinon on affiche l’erreur…

Etape 2 : configuration de Struts

Je considère dans ce chapitre que Struts est déjà correctement installé. Si vous avez besoin d’un projet vide, vous pouvez utiliser le Struts-Blank.

On commence par déclarer dans le fichier struts-config, une nouvelle action correspondant à notre appel depuis le javascript :

  <action-mappings>    	
<!- - Declaration de notre action Struts -->        
<action            path="/ajaxAction"            type="com.jc.AjaxAction" />    
</action-mappings>

Le contenu de l’action mapping est relativement simple puisque on se contente d’appeler une classe Java qui effectuera le traitement serveur. On peut remarquer qu’il n’est pas nécéssaire de préciser une page jsp de résultat puisque c’est le code java qui écrit dans l’objet response.

Etape 3 : Ecriture de l’action Struts

Le code de l’action Struts est le suivant :

package com.jc; import java.io.PrintWriter; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import org.apache.struts.action.Action; 
import org.apache.struts.action.ActionForm; 
import org.apache.struts.action.ActionForward; 
import org.apache.struts.action.ActionMapping; 

public class AjaxAction extends Action { 	
 public ActionForward execute(ActionMapping mapping, ActionForm form,        HttpServletRequest request, HttpServletResponse response) throws Exception 
 {
  String valeur = "test"; 	    
  // Write the text to response 	   
  response.setContentType("text/html"); 	   
  PrintWriter out = response.getWriter(); 	   
  out.println(valeur); 	  
  out.flush(); 	   
  return null; 	
 } 
}

Le code est relativement simple, dans le sens où il se contente d’écrire dans le flux de sortie de la requête HTTP. On peut remarquer que le return est null, car on ne suit pas la logique des mappings Struts et que le Javascript lira directement le flux provenant du serveur.

Etape 4 : Le test

En ouvrant le navigateur sur la page de test on tombe sur cet écran :

Après un clic sur le bouton, la popup d’alert s’affiche avec le résultat retourné par le serveur :

Parfait, maintenant on implémente un peu de fonctionnel…

Etape 5 : Implémentation de l’affichage de l’heure du serveur

Maintenant que l’aller retour serveur fonctionne, on peut ajouter un peu de code applicatif pour montrer l’intérêt de ce framework. Dans notre exemple, au clic sur le bouton, le serveur va retourner son heure système qui sera affiché sur la page.

La première modification concerne le code de l’action Java :

A la place de :

String valeur = "test";

Récupérons plutôt l’heure système (attention aux imports) :

Date date = GregorianCalendar.getInstance().getTime(); 
String valeur = SimpleDateFormat.getDateTimeInstance().format(date);

Il ne reste plus qu’à modifier le javascript de la page : Dans le traitement du résultat :

if (req.status == 200) 
{ // OK response       
// Pour le debug       
alert("ok:"+req.responseText);

On remplace l’alert par :

  // alert("ok:"+req.responseText);       
  var d = document.getElementById('resultat');       
  d.innerHTML = req.responseText;

résultat sur la page :

conclusion

Cette petite introduction sera j’espère pratique pour les personnes souhaitant d’intégrer rapidement des requêtes asynchrones sur des applications déjà existantes. Mais pour une intégration plus poussée, l’utilisation de frameworks dédiés sera un gros plus.

Source du projet développé sous WTP 1.0 : AjaxProject.zip

bugs possibles

  • Attention à l’url de destination passé à la méthode Javascript dans un environnement Struts. Une solution est de récupérer dans le Javascript la base de l’application avec un code comme celui-ci :
var path = window.location.pathname; path = path.substring( 0, path.length - "actionCourante.do".length );
  • J’ai remarqué également un autre problème très génant avec le cache d’IE5/IE6 sous Windows 2000 (qui bizarrement fonctionnait sous IE6 – Windows XP). En fait, le navigateur mettait en cache les appels depuis XmlHTTPRequest : Résultat, plus rien ne se passait si on appelait 2x la même URL. Pour corriger ce problème, j’ai ajouté dans les paramètres d’appel :
req.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");

Références intéressantes


Billets similaires

Actions

Informations

29 réponses à “AJAX et Struts par l’exemple”

11 01 2006
Benjamin Francisoud (10:04:29) :

Très bon article 😉

11 01 2006
Christophe (18:28:59) :

Oui, tres bon article en effet, je regrete juste que la question de l ambiguite de la relation entre Ajax et Achille ne soit pas plus longuement evoquee ainsi que celle, cruciale, de la position de Patrocle dans cette affaire… Quoi qu il en soit, j en profite pour vous souhaiter une tres bonne annee a tous…

11 01 2006
Julien (19:33:43) :

Merci benjamin, venant de toi, je sais ce que ca vaut 🙂

Christophe, effectivement j’ai manqué de matière sur ce sujet, j’ai également peu fait part de mes conseils éclairés en nettoyage de sol…

Et bonne année aussi !

24 01 2006
Le weblogue de SeB (01:14:10) :

Ajax & Struts – en quelques lignes

Très simple et très efficace. A consulter par les personnes souhaitant tester rapidement ce genre d’intégration.

13 02 2006
François (11:51:52) :

Merci julien,

Ton article m’a permis de rajouter la foncitionnalité "qui tue" en quelques minutes.

Continue..
c toujours mieux quand ça brille.

3 03 2006
Jesterscap_88 (12:15:49) :

Tres bon tuto ! Clair, simple et fonctionnel ! 🙂

Merci !

16 03 2006
ToMaZi (09:06:16) :

Merci, c’est un bon tutorial puisque ca marche quand on l’a suivi ! :-p

merci

19 03 2006
sam (14:41:48) :

salut,

Je trouve l’exemple bien pratique. Je voulais l’executer chez moi et là
je reçois un l’erreur :
La servlet (action) est actuellement indisponible.
Pourtant mes fichiers web.xml et struts-config.xml sontbien configurés.

Une idée ?
Merci

20 03 2006
Julien (10:38:48) :

Sam : Je peux jeter rapidement un coup d’oeil au projet mais il faudrait que tu m’envois les sources. A mon adresse mail par ex. (voir sur le panneau de droite)

Julien Carnelos

19 04 2007
NOEL (09:22:07) :

Article sympa pour une première approche d’AJAX avec STRUTS.

12 06 2007
James Wood (13:16:26) :

Bien sympa ce code, mais dommage qu’il ne fait pas état de l’envoi de données de formualire via appel ajax (en POST bienentendu )

4 07 2007
kalf (16:32:24) :

Très bon tuto en effet,
par contre j’ai un p’tit sushi, il me met « Invalid path was requested /ajaxAction »
au niveau de l’étape 4.

pourtant je pense l’avoir bien implémenter sur mon serveur tomcat…

snif :'(

5 07 2007
kalf (13:58:44) :

bon ok ca fonctionne maintenant,
mais j’ai une petite question.

comment faire pour passer un paramètre (une chaine de caractères par exemple) entre le client et le serveur.
je suppose qu’on le récupère via l’objet httpservletrequest (dans l’action) mais j’ai pas la bonne méthode …

9 07 2007
abdell (15:47:22) :

Merci, c’est un article tres bon et tres interresson mais j un ptit probleme dans l’execution: il m’affiche les deux message d’erreur « action servlet is not available » et le deuxieme « status 404 »!!!!
Merci

18 07 2007
Nicola (03:46:27) :

Merci, c’est très apprécié d’avoir un exemple simple et concret avec Struts.

3 09 2007
slim (15:27:55) :

Trés bon tutorial simple et complet.
Juste une question, comment configurer l’appel ajaxCallRemotePage pour une méthode spécifique d’une action lookupAction .Merci d’avance.

27 09 2007
ziss (17:11:04) :

Trés bon tutorial il m’a permis en quelques minutes de comprendre le principe de fonctionement Struts Ajax… Question quand vous dite:
* Ecrire des servlets spécifiques
*………………
* Ecrire des actions Struts “standard” dédiés
La première et la troisième ne sont-ils pas les même aproches?? sinon comment se présenterait la première (en quelques mots)
merci encore une fois.

27 09 2007
Atma (21:08:38) :

Je pense que ce que julien voulait dire par « Ecrire des servlets spécifiques », c’était « un traitement ajax = une servlet spécifique » (ie « on se sert pas de la ‘magie’ de Struts »).
En gros, plutôt que de faire un appel à « monAction.do », on appelerait directement une servlet, et le traitement « métier » (enfin là, je met des guillemets parce qu’on est quand même très coté présentation là) se ferait dans le doGet() de cette dernière.

Maintenant, là où je te rejoins lorsque tu parles de « même approche », c’est que le fait d’utiliser Struts nous fait n’avoir qu’une seule Servlet (la DispatchServlet) au lieu de tout un tas dans l’approche « écrire des servlets spécifiques ». Cependant, on aura « tout un tas » d’actions Struts (=> un p’tit peu la même approche … mais un peu plus propre puisque Struts offre quand même une certaine souplesse dans la réutilisabilité des Actions (action forwarding) que l’API servlet n’offre pas « aussi facilement »).

26 10 2007
ziss (12:35:59) :

Hi,
Je voudrais savoir est ce qu’il est possible d’appeler une méthode particulaire de l’action depuis le javaScript.
Merci

26 10 2007
Valérie (14:22:24) :

enfin un tuto pour les nanas … clair, simple et précis … comme une bonne recette de cuisine 😉
Merci bcp

30 11 2007
stephane (11:47:29) :

Trés bon tuto.
clair, efficace : ça prouve la compréhension de l’auteur

merci beaucoup

7 01 2008
Zeng Stéphane (18:21:47) :

Salut,
je voulais juste savoir comment faire lorsque le résultat de notre requete est dans un form bean. Les forms beans ne font pas partie de la réponse à la requete, alors comment faire ?

26 02 2008
revo (10:09:33) :

lu

Purée, c’est très simple à mettre en place ^^’

Je tiens à signaler que tu as oublié de préciser qu’il fallait mettre dans le code pour afficher le resultat

Je vous recommande d’utiliser firebug pour le dev avec ajax, c’est très pratique

26 02 2008
revo (10:11:24) :

oups non c’etait noté sur la premiere page mais j’ai zaapé

7 04 2008
Adnane (16:20:06) :

on est en 2008 et je vous avoue que je n’ai pas trouvé grand chose sur comment intégrer ajax avec struts.
merci c’est trés bien fait.

9 04 2008
Tanebisse (14:05:47) :

Même question que Zeng Stéphane, comment faire pour récupérer le form dans notre action Struts car notre bouton envoyer avec la fonction appelAjaxExemple ne renvoie pas le formulaire !!!

9 07 2008
LE Kim (10:18:32) :

Bonjour,

Je suis débutante de Strust ajax, comment configuartion Strust et Ajax pour faire un petit programme de test en dessus.

Merci de donner les infos.

Kim LE

24 08 2008
bloc (13:15:22) :

Merci pour le tuto

31 07 2009
Julien (17:46:08) :

Bonjour,

grand merci, je suis dans une boite où est installé Windows 2000, je ne comprenais pas pourquoi je n’arrivais pas à récupérer mes infos, alors que ca fonctionnait sur un autre os !

Le setRequestHeader(« If-Modified-Since », « Sat, 1 Jan 2000 00:00:00 GMT »); me sauve !

Encore merci 😉