Technologies

Struts2 : Base de connaissance technique

Publié le : Auteur: Alexandre BOZAS
struts2[1]

Pourquoi un article sur Struts2 ?

Mon but était de monter en compétence sur la dernière version Struts2 (2.3.16) et de revoir les différents concepts liés à Struts2 :

  • Chargement d’un bean dans une page
  • Envoi des informations d’une page JSP côté serveur
  • Librairies de tags Struts
  • Internationalisation dans Struts

Tout en apportant un point d’attention sur la gestion des sessions utilisateurs via :

  • Les intercepteurs
  • L’interface SessionAware

Description des concepts

Les concepts Struts2 présentés correspondent uniquement à ceux utilisés dans le projet de test qui sera présenté dans la suite de l’article (donc non exhaustifs).

Filter Struts2

L’objet Filter de Struts2 (FilterDispatcher ou StrutsPrepareAndExecuteFilter) va être l’objet Filter qui va intercepter toutes les requêtes HTTP et effectuer les opérations nécessaires au bon fonctionnement de Struts2.
ATTENTION : FilterDispatcher est déprécié depuis la version 2.1.3. Le nouveau filtre à déclarer est StrutsPrepareAndExecuteFilter pour cette version.
http://struts.apache.org/release/2.3.x/docs/webxml.html

Mécanisme d’intercepteur

Fichiers struts.xml, AuthenticationInterceptor.java
Le mécanisme d’intercepteur permet de d’exécuter du code avant ou après l’invocation de l’action.
En effet, cela peut être utile pour vérifier qu’un utilisateur est authentifié lors des accès à des actions Struts.
Un schéma très clair sur le site de Struts2 montre l’appel de l’intercepteur avant l’appel de l’action.
Effectivement, c’est dans l’intercepteur qu’on décide oui ou non
http://struts.apache.org/release/2.3.x/docs/interceptors.html

Récupération dans une Action de la requête/réponse HTTP, session, …

Fichiers ManageUserAction.java, AbstractAction.java

Struts2 propose une série d’interface permettant de récupérer dans les actions par référence les objets du type requête/réponse HTTP ou session :

  • SessionAware permet de récupérer la Map des attributs de la session HTTP
  • ServletRequestAware permet de récupérer une référence sur l’objet HTTPServletRequest
  • ServletResponseAware permet de récupérer une référence sur l’objet HTTPServletResponse

Interface ModelDriven

Fichiers ManageUserAction.java, AuthenticateAction.java
Au contraire de Struts1, Struts2 n’a plus de Form.
Les actions doivent implémenter cette interface pour récupérer les valeurs d’un bean depuis les pages JSP côté serveur.
http://struts.apache.org/release/2.3.x/docs/model-driven.html

Afficher un bean / Récupérer les valeurs pour un bean (dans une page JSP)

Struts2 se base dans les pages JSP :

  • Sur les attributs value= »… » pour afficher les valeurs d’un bean dans une page JSP.
  • Sur les attributs name= »… » pour renvoyer les valeurs d’une page JSP vers un bean.

Système de plugin

Struts2 propose un système de plugin afin de pouvoir étendre les fonctionnalités du framework de base.
Je n’ai pas expérimenté dans ce projet le système de plugin, mais il est important si on souhaite utiliser Struts2 avec d’autres Frameworks comme JSF, Spring, Tiles ou autre.

Réalisation du projet d’exemple

Description

Ce projet est une application simple de gestion d’utilisateurs composé :

  • D’un écran de login
  • Une page d’accueil
  • Un écran de gestion des utilisateurs
  • Un écran d’affichage/modifications des caractéristiques d’un utilisateurs

La fonction de login est intégré via un fragment JSP qui se retrouvera dans toutes les autres pages pour vérifier que l’utilisateur reste logué

Détail technique du projet

L’objectif de ce projet de démo étant de se focaliser sur Struts2 (2.3.16), j’ai donc décidé de :

  • L’utilisation unique de Struts2 en terme de dépendances Maven (Pas de frameworks d’injection de dépendances ni de logging).
  • L’utilisation d’un service bouchonné de gestion d’utilisateurs avec une Map statique plutôt qu’ajouter une couche de persistance de données.

Ci-après la base de tutoriel utilisé avec la modification suivante :

  • Suppression de l’interface UserAware qui est inutile car un objet représentation un utilisateur (username et roles) est stocké dans la session.

Ci-après le lien vers le projet
On accède à l’application une fois déployée via l’URL http://localhost:8080/Struts2Example/pages/login.jsp

Problèmes rencontrés

J’ai rencontré plusieurs problèmes lors de mes travaux comme :

  • Des problèmes avec l’internationalisation sur les tags JSP Struts via l’attribut « key ».
  • Des problèmes avec certains tags (s:textfield) pour la récupération côté serveur.

Le détail des problèmes techniques rencontrés est décrits plus en détail plus bas dans la partie Annexe Technique : Détail problèmes rencontrés.

Conclusion

Il est important de ne pas considérer l’utilisation unique de Struts2 comme effectué dans ce projet exemple mais aussi de l’associer à l’utilisation d’autres frameworks comme par exemple Tiles pour la partie templating.

De plus, Struts2 n’est pas le seul framework Web orienté Action. Il existe aussi par exemple Spring-MVC qui permet une meilleure intégration avec le framework Spring et sur lequel je me dirigerai plus si je devais démarrer un projet étant donné aussi les problèmes rencontrés lors de mes travaux.

Il faut aussi considérer en terme d’architecture les cas d’utilisation avant de se porter sur ces frameworks et considérer par exemple si l’architecture nécessite un framework Web orienté Action (usecases : beaucoup de workflow, spécificité sur le traitement de la requête ou réponse HTTP) ou Component (usecases : peu de workflow et beaucoup d’intéraction de composants dans la page).

Références Utiles

MyKong Struts2 Tutorials qui présente des exemples très concrets des exemples clairs et simples de l’utilisation de Struts2.
La documentation Struts2 qui va donner des explications plus poussées sur l’utilisation des différents composants de Struts2 ainsi que des éléments d’architecture
Avis sur un forum sur les différences entre frameworks Web orientés Action et Component

Pour continuer

Ce projet de démonstration n’est pas terminé bien entendu et sera à enrichir avec :

  • La protection des ressources (mise en place de différents namespaces en fonction des rôles + déclaration dans web.xml via la balise security-constraint)
  • L’utilisation de validations Struts
  • Les différents éléments graphiques fournis par Struts2 à utiliser
  • L’utilisation de AJAX dans Struts2.

Annexe Technique : Détail problèmes rencontrés

Q : Impossibilité d’utiliser du JSTL dans les tags Struts pour des raisons de sécurité :

R : http://stackoverflow.com/questions/21005849/access-struts2-action-with-jstl

Q : Les expressions JSTL n’arrivent pas à être évaluées.

R : Pour cela, il faut ajouter la directive <%@ page isELIgnored= »false »%>
http://www.velocityreviews.com/forums/t133907-jstl-and-c-out-value-not-being-evaluated.html

Q : Comment inclure un fragment JSP qui contient le contexte de la page parent ?

R : Utiliser <%@include file= »… »%> plutôt que <s:include value= »… »/> pour l’inclusion des fragments JSP :
– La 1ère est incluse lors de la compilation de la JSP et permet par conséquent à la page incluse d’hériter du contexte parent
– La 2ème est incluse à « l’exécution » et donne par conséquent à la page incluse un contexte qui lui est propre ce qui implique une impossibilité d’accéder aux variables de la page parente.
http://stackoverflow.com/questions/943770/variables-in-jsp-pages-with-included-pages

Q : Unexpected Exception caught setting ‘welcome.button.manageuserwithform’ on ‘class com.struts.login.example.action.ManageUserAction: Error setting expression ‘welcome.button.manageuserwithform’ with value ‘[Ljava.lang.String;@165c0a56’

R : Le problème vient de l’interprétation de Struts du tag « s:submit ». En effet, <s:submit key= »btn.search » /> génèrera le code HTML <input type= »submit » name= »btn.search » value= »Search »/> ce qui implique que l’action a besoin de cette valeur (même si ce n’est pas le cas).
La solution est alors d’utiliser la syntaxe suivante afin de gérer ce cas : <s:submit value= »%{getText(‘btn.search’)} »/>
http://struts.1045723.n5.nabble.com/labelizing-submit-buttons-td3485487.html

Q : J’ai le contexte et le namespace qui sont répétés quand j’utilise les tags <s:url> et <s:a>

R : La création de <s:url> conserve le contexte et le namespace ce qui rend son utilisation non possible avec les tags <s:a>
<s:url action= »deleteUser »><s:param name= »id »><s:property value= »id »/></s:param></s:url>
<s:a action= »%{deleteUser} »>Delete</s:a>
Génèrera une URL /Struts2Example/pages/Struts2Example/pages/deleteUser.action?id=2
La résolution de ce problème peut être fait en supprimant dans l’url le contexte et namespace de la manière suivante :
<s:url action= »deleteUser » var= »deleteUser » includeContext= »false » namespace= » »><s:param name= »id »><s:property value= »id »/></s:param></s:url>

Q : Les balises <s:textfield> dont l’attribut disable est à true ne sont pas renvoyés dans le bean.

R : Un workaround est de stocker cette valeur dans un champs hidden en plus du champ textfield.
http://www.coderanch.com/t/51370/Struts/textfield-disabled-form-bean